一、文件描述符
1.1 文件描述符介绍
当调用open 函数打开一个现有文件或创建一个新文件时,内核会向进程返回一个文件描述符,用于指代被打开的文件,所有执行IO操作的系统调用都是通过文件描述符来索引对应的文件。在C代码中,文件描述符是一个int 类型变量。
1.2 进程最大文件数
在Linux 中,一个进程可以打开的文件数是有限制的,可以通过命令ulimit 来查看进程可打开的最大文件数,用法如下:
ulimit -n
这里最大值默认是1024.
1.3 三个特殊文件描述符
文件描述符 | 具体文件 |
---|---|
0 | 系统标准输入(一般指键盘) |
1 | 系统标准输出 (一般指LCD显示器) |
2 | 系统标准错误 (一般指LCD 显示器) |
所以,我们程序中调用open函数打开文件的时候,分配的文件描述符一般都是从3开始。 |
二、常见文件IO函数
2.1 open
在Linux 系统中要操作一个文件,需要先打开该文件,得到文件描述符,然后再对文件进行相应的读写操作,最后再关闭该文件。当打开的文件不存在时,可以配置创建一个新的文件,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);
2.1.1 函数参数以及返回值
2.1.1.1 pathname
字符串类型,用于标识需要打开或创建的文件,可以包含路径信息。例如: ./test_file
2.1.1.2 flags
调用open 函数时需要提供的标志,包括文件访问模式标志以及其它文件相关标志,这些标志使用宏定义进行描述,都是常量。我们传入的flags 参数既可以单独使用某一个标志,也可以通过位或运算(|)将多个标志进行组合。open 函数的第三个参数,只有在使用了O_CREAT 或O_TMPFILE 标志时才有效。 常见标志介绍如下:
文件访问方式标志
- O_RDONLY 以只读方式打开文件
- O_WRONLY 以只写方式打开文件
- O_RDWR 以可读可写方式打开文件
这三个是文件打开方式标志,传入的flags 参数中必须要包含其中一种标志,而且只能包含一种。
标志 | 用途 | 说明 |
---|---|---|
O_CREAT | 如果pathname参数指向的文件不存在则创建此文件 | 使用此标志时,调用open函数需要传入第三个参数mode,参数mode用于指定新建文件的访问权限。 |
O_DIRECTORY | 如果pathname 参数指向的不是一个目录,则调用open失败 | |
O_EXCL | 此标志一般结合O_CREAT 标志一起使用,用于专门创建文件。在flags 参数同时使用到了O_CREAT 和O_EXCL 标志的情况下,如果pathname 参数指向的文件已经存在,则open函数返回错误 | 可以用于测试一个文件是否存在,如果不存在则创建此文件,如果存在则返回错误。这使得测试和创建两者成为一个原子操作。 |
O_NOFOLLOW | 如果pathname 参数指向的是一个符合链接,将不对其进行解引用,直接返回错误 | 不加此标志情况下,如果pathname 参数是一个符合链接,会对其进行解引用 |
不同内核版本所支持的flags 标志是存在差异的。例如说新版本内核所支持的标志可能老版本就不支持的,亦或者老版本支持的标志在新版本已经被取消、替代,man手册中对一些标志是从哪个版本开始支持的有简单地说明。 |
2.1.1.3 mode
宏定义 | 说明 |
---|---|
S_IRUSR | 允许文件所有者读文件 |
S_IWUSR | 允许文件所有者写文件 |
S_IXUSR | 允许文件所有者执行文件 |
S_IRWXU | 允许文件所有者读写执行文件 |
S_IRGRP | 允许同组用户读文件 |
S_IWGRP | 允许同组用户写文件 |
S_IXGRP | 允许同组用户执行文件 |
S_IRWXG | 允许同组用户读、写、执行文件 |
S_IROTH | 允许其他用户读文件 |
S_IWOTH | 允许其他用户执行文件 |
S_IXOTH | 允许其他用户执行文件 |
S_IRWXO | 允许其他用户读、写、执行文件 |
2.2 read
调用read 函数可从打开的文件中读取数据。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
函数参数和返回值含义如下:
- fd : 文件描述符
- buf:指定读入数据对应的缓冲区。
- count:指定需要读取的字节数。
- 返回值:如果读取成功将返回读取到的字节数,实际读取到的字节数可能会小于count参数指定的字节数,也有可能会为0,例如进行读操作时,当前文件偏移量已经到了文件末尾。实际读取到的字节数小于要求读取的字节数,例如在到达文件末尾之前有30个字节数据,而要求读取100个字节,则read读取成功只能返回30;而再次再调用read读,它将返回0(文件末尾)。
2.3 write
调用write 函数可向打开的文件写入数据,其函数原型如下:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
函数参数和返回值:
- fd : 文件描述符
- buf:指定写入数据对应的缓冲区。
- 返回值:如果成功将返回写入的字节数(0 表示未写入任何字节);如果此数字小于count 参数,这不是错误,可能是磁盘空间已满。如果返回-1,表示写入出错。
2.4 close
可调用close 函数关闭一个已经打开的文件。
#include <unistd.h>
int close(int fd);
函数参数和返回值:
- fd : 需要关闭的文件的描述符。
- 返回值:成功返回0;失败返回-1;在Linux 系统中,当一个进程终止时,内核会自动关闭它打开的所有文件。
2.5 lseek
对于每个打开的文件,系统都会记录它的读写位置偏移量,当调用read()或write 函数对文件进行读写操作时,就会从当前读写位置偏移量开始进行数据读写。当打开文件时,会将读写偏移量设置为指向文件开始位置处,以后每次调用read()、write()会自动对其进行调整,以指向已读或已写数据后的下一个字节。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
函数参数和返回值
- fd : 文件描述符
- offset : 偏移量,以字节为单位。
- whence:用于定义参数offset 偏移量对应的参考值。该参数为下列其中一种:
参数 | 说明 |
---|---|
SEEK_SET | 读写偏移量将指向offset 字节位置处(从文件头部开始算) |
SEEK_CUR | 读写偏移量将指向当前位置偏移量+offset 字节位置处,offset 可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移 |
SEEK_END | 读写偏移量将指向末尾 + offset 字节位置处,offset 可以为正,也可以为负。 |