文件与IO(1)-读写访问

目录

 

什么是I/O

文件描述符

文件指针与文件描述符的转换

文件系统调用

文件的随机读写

目录访问


什么是I/O

输入/输出是内存和外设之间拷贝数据的过程:

   设备->内存: 输入操作

   内存->设备: 输出操作

 高级I/O: ANSI C提供的标准I/O库函数成为高级I/O, 也称为带缓冲的I/O;

 低级I/O: Linux 提供的系统调用, 通常也称为不带缓冲的I/O;

文件描述符

对于Linux而言,所有对设备或文件的操作都是通过文件描述符进行的。

当打开或者创建一个文件的时候,内核向进程返回一个文件描述符(非负整数)。后续对文件的操作只需通过该文件描述符,内核记录有关这个打开文件的信息。

一个进程启动时,默认打开了3个文件,标准输入、标准输出、标准错误,对应文件描述符是0STDIN_FILENO)、1STDOUT_FILENO)、2STDERR_FILENO,这些常量定义在unistd.h头文件中。

 其中, 文件描述符基本上是与文件描述指针(FILE*)一一对应的, 如文件描述符0,1,2 对应 stdin, stdout, stderr文件指针

文件指针与文件描述符的转换

fileno:将文件指针转换为文件描述符   int fileno(FILE *stream);

fdopen:将文件描述符转换为文件指针   FILE *fdopen(int fd, const char *mode);

//示例
int main()
{
    cout << "fileno(stdin) = " << fileno(stdin) << endl;
    cout << "fileno(stdout) = " << fileno(stdout) << endl;
    cout << "fileno(stderr) = " << fileno(stderr) << endl;
    return 0;
}

文件系统调用

open系统调用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

参数:

   pathname:  文件名, 可以包含[绝对/相对]路径名;

   flags: 文件打开模式;

   mode: 用来指定对文件所有者, 文件用户组以及系统中的其他用户的访问权限;

返回值

     打开成功,返回文件描述符

     打开失败,返回-1

文件的随机读写

打开方式

文件权限

//示例1
#define ERR_EXIT(m) \
       do \
	   { \
	   perror(m); \
	   exit(EXIT_FAILURE); \
	   }while(0)
	   
int main()
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
    {
        cerr << "file open error, errno = " << errno <<
             "\nstrerror: " << strerror(errno) << endl;
        perror("perror");
        exit(EXIT_FAILURE);
    }
    if(fd==-1)
	{
	    perror("open error");
        exit(EXIT_FAILURE);
	}
    if(fd==-1)
	ERR_EXIT("open error");
	
    cout << "file open success" << endl;
}

如何使得打开文件时创建文件呢?使用带有三个参数的open,权限newmode=mode&~umask

//示例2
inline void err_exit(std::string message)
{
    perror(message.c_str());
    exit(EXIT_FAILURE);
}
 
int main()
{
    umask(0000);
    int fd = open("test.txt", O_RDWR|O_CREAT|O_EXCL, 0666);
    if (fd == -1)
        err_exit("file open error");
    else
        cout << "file descriptor = " << fd << endl;
}

(1). umask API

   //改变umask值

   mode_t umask(mode_t mask);

(2). ulimit -a

   查看系统中的各种限制;

   其中-n: 查看一个进程所能够打开的最大文件数

(3).cat /proc/sys/fs/file-max 

   查看一个系统能够支持的最大打开文件数(该数与内存大小有关)

close系统调用

#include <unistd.h>
int close(int fd);
关闭文件描述符, 使得文件描述符得以重新利用

creat系统调用

Linux也提供可选的创建文件的系统调用,它称为creat()

函数原型:

  int creat(const char *path, mode_t mode);

参数

         path :文件的名称,可以包含(绝对和相对)路径

        mode:  用来规定对该文件的所有者,文件的用户组及系           统中其他用户的访问权限

       返回值

  打开成功,返回文件描述符;

  打开失败,返回-1

creat()调用  fd = creat(file, mode)  完全等价于近代的open()调用  fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode)

read系统调用

一旦有了与一个打开文件描述相连的文件描述符,只要该文件是用O_RDONLYO_RDWR标志打开的,就可以用read()系统调用从该文件中读取字节 

	ssize_t read(int fd, void *buf, size_t count);

参数

fd :想要读的文件的文件描述符

buf 指向内存块的指针,从文件中读取来的字节放到这个内存块中

count 从该文件复制到buf中的字节个数

返回值

         如果出现错误,返回-1

         读文件结束,返回0

         否则返回从该文件复制到规定的缓冲区中的字节数

write系统调用

     write()系统调用将数据写到一个文件中

ssize_t write(int fd, const void *buf, size_t count);

函数参数:

-fd:要写入的文件的文件描述符

-buf: 指向内存块的指针,从这个内存块中读取数据写入        到文件中

-count: 要写入文件的字节个数

返回值

  如果出现错误,返回-1

  如果写入成功,则返回写入到文件中的字节个数

注意:

   write返回大于0时, 并不代表buf的内容已经写入到磁盘上的文件中了, 其仅仅代表buf中的数据已经copy到相应的内核缓冲区了. 要实现将缓冲区的内容真正”冲洗”到磁盘上的文件, 需要调用fsync函数;  int fsync(int fd);其将内核缓冲区中尚未写入磁盘的内容同步到文件系统中;其实在open调用的时候也可以指定同步选项:O_SYNC ,write会等到将buf的内容真正的写入到磁盘才真正返回;

//示例: 带有O_SYNC选项
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        cerr << "Usage : " << argv[0] << " src dest" << endl;
        exit(EXIT_FAILURE);
    }
 
    int infd = open(argv[1], O_RDONLY);
    if (infd == -1)
        err_exit("file O_RDONLY error");
    int outfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666);
    if (outfd == -1)
        err_exit("file O_WRONLY error");
 
    char buf[1024];
    int readBytes, writeBytes;
    while ((readBytes = read(infd, buf, sizeof(buf))) > 0)
    {
        writeBytes = write(outfd, buf, readBytes);
        cout << "readBytes = " << readBytes
             << ", writeBytes = " << writeBytes << endl;
    }
   close(infd);
   close(outfd);
}

文件的随机读写

  • 到目前为止的所有文件访问都是顺序访问。这是因为所有的读和写都从当前文件的偏移位置开始,然后文件偏移值自动地增加到刚好超出读或写结束时的位置,使它为下一次访问作好准备。
  • 有个文件偏移这样的机制,在Linux系统中,随机访问就变得很简单,你所需做的只是将当前文件移值改变到有关的位置,它将迫使一次read()write()发生在这一位置。(除非文件被O_APPEND打开,在这种情况下,任何write调用仍将发生在文件结束处)

lseek系统调用

功能说明:通过指定相对于开始位置、当前位置或末尾位置的字节数来重定位 curp,这取决于 lseek() 函数中指定的位置

off_t lseek(int fd, off_t offset, int whence);
// fd文件标识符  offest 偏移量   whence 搜索的起始位置

返回值:返回新的文件偏移值

whence 表示搜索的起始位置,有以下几个值:(这些值定义在<unistd.h>

//示例1
int main(int argc, char *argv[])
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1)
        err_exit("open error");
    char buf[1024] = {0};
    int readBytes = read(fd, buf, 5);
    cout << "readBytes = " << readBytes << ", buf: " << buf << endl;
    int seekCount = lseek(fd, 0, SEEK_CUR);
    cout << "current offset = " << seekCount << endl;
}
//示例2: 产生空洞文件
int main(int argc, char *argv[])
{
    int fd = open("hole.txt", O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0666);
    if (fd == -1)
        err_exit("open error");
 
    if (write(fd, "ABCDE", 5) == -1)
        err_exit("first write error");
    //创建一个1G的文件
    if (lseek(fd, 1024*1024*1024, SEEK_CUR) == -1)
        err_exit("lseek error");
    if (write(fd, "Hello", 5) == -1)
        err_exit("second write error");
    close(fd);
}

[附]

-查看hole.txt文件

  ls  -l hole.txt

  od -c hole.txt

  cat -A hole.txt

-查看该文件大小(在磁盘中)

  du -h hole.txt

  du -b hole.txt

  du -k hole.txt

  du -m hole.txt

注意文件的大小与在磁盘中大小是不一样的,磁盘单位是块(4k)

目录访问

opendir:打开一个目录

       #include <sys/types.h>
       #include <dirent.h>
       DIR *opendir(const char *name);

返回值:

   成功: 返回目录指针;

   失败: 返回0;

readdir:访问指定目录中下一个连接的细节原型

struct dirent *readdir(DIR *dirp);

返回值:

   成功: 返回一个指向dirent结构的指针, 它包含指定目录的下一个连接的细节;

   没有更多连接时, 返回0;

struct dirent
{
    ino_t          d_ino;       /* inode number */
    off_t          d_off;       /* not an offset; see NOTES */
    unsigned short d_reclen;    /* length of this record */
    unsigned char  d_type;      /* type of file; not supported
                                         by all filesystem types */
    char           d_name[256]; /* filename */
};

closedir: 关闭目录  

int closedir(DIR *dirp);

返回值:调用成功返回0,失败返回-1

//示例: 简单的ls程序
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        cerr << "Usage : " << argv[0] << " <directory>" << endl;
        exit(EXIT_FAILURE);
    }
 
    DIR *dir = opendir(argv[1]);
    if (dir == NULL)
        err_exit("opendir error");
 
    struct dirent *ent;
    while ((ent = readdir(dir)) != NULL)
    {
        //过滤掉隐藏文件
        if (ent->d_name[0] == '.')
            continue;
        cout << ent->d_name << "\ti-node: " << ent->d_ino
             << ", length: " << ent->d_reclen << endl;
    }
    closedir(dir);
}

mkdir系统调用:用来创建一个称为pathname的新目录,它的权限位设置为mode

int mkdir(const char *pathname, mode_t mode);

返回值:调用成功返回0,失败返回-1

rmdir系统调用 :删除一个空目录原型:

int rmdir(const char *pathname);

返回值:调用成功返回0,失败返回-1

chmod和fchmod系统调用:用来改变给定路径名pathname的文件的权限位

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

返回值:调用成功返回0,失败返回-1

chown和fchown系统调用:用来改变文件所有者的识别号(owner id)或者它的用户组识别号(group ID)

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);

返回值:调用成功返回0,失败返回-1

猜你喜欢

转载自blog.csdn.net/alatebloomer/article/details/81301731