文件的基本原理
当进程每打开一个文件,内核会为其返回一个文件描述符fd,是一个非负int整数,之后对文件的操作需要以fd作为主要参数。一般从0算起,上限为1024-1,但可以设定上限值。
每个进程默认会打开三个文件,其文件描述符分别为STDIN_FILENO STDOUT_FILENO STDERR_FILENO
,即0 1 2,分别表示标准输入、标准输出、标准错误。
文件描述符不仅能表示普通文件,还能表示设备文件、目录、socket等等。
打开文件
open()系统调用
int open(const char *name, int flags, mode_t mode);
将路径name所指定的文件映射至一个文件描述符并返回,flags可取O_RDONLY O_WRONLY O_RDWR
,只读、只写、读写。文件位置会设置到开头。
flags上述三种打开文件的方式还可以与以下常用参数进行按位逻辑或。
flags | 描述 |
---|---|
O_APPEND |
附加模式打开 |
O_CREAT |
如果文件不存在,则创建,如果存在则需要同时指定O_EXCL将会调用失败 |
O_EXCL |
如果指定O_CREAT且文件存在,则此标志会导致调用失败 |
O_DIRECT |
打开文件以便进行直接I/O |
O_NOFOLLOW |
如果name是一个符号链接,则会调用失败,但如果目录是符号链接,文件不是,也会打开成功 |
O_NONBLOCK |
以非阻挡模式打开文件,即进程在open时或者其它操作时都不会阻挡正在进行的I/O,此行为只能用于定义FIFO |
只要不是在创建文件,则mode可以忽略。
在创建文件时需要设定文件使用权限,也是按位逻辑或。
mode | 描述 |
---|---|
S_IRWXU |
拥有者具有读取、写入和执行的权限 |
S_IRUSR |
拥有者具有读取的权限 |
S_IWUSR |
拥有者具有写入的权限 |
S_IXUSR |
拥有者具有执行权限 |
S_IRWXG |
组具有读取、写入和执行权限 |
S_IRGRP |
组 读取 |
S_IWGRP |
组 写入 |
S_IXGRP |
组 执行 |
S_IRWXO |
其他人 读写执行 |
S_IROTH |
其他人 读 |
S_IWOTH |
其他人 写 |
S_IXOTH |
其他人 执行 |
int fd = open("/home/test", O_WRONLY | O_APPEND)
creat()系统调用
int creat(const char *name, mode_t mode);
read()系统调用
ssize_t read(int fd, void *buf, size_t len);
从fd所引用的文件的当前位置读取len个字节到buf,执行成功返回写入buf的字节数目,执行失败返回-1并设定errno。文件位置会前进到fd所读取的字节数目。
如果fd代表的是一个字符设备文件,则每次只会从当前位置读取数据。
read过程会产生的错误
在read过程中会出现以下情况:
- 返回值等于len,即预期结果。
- 返回值小于len,但大于0,可能原因时可读取的字节数小于len,或者收到系统中断信号。
- 返回0,表示EOF,到达了文件末尾。
- 返回-1,errno=EINTR,在读取前收到中断。
- 返回-1,error=EAGAIN,表示读取操作被阻挡,因为可供读取的数据尚未出现,稍后应该再次进行调用,此情况只发生在非阻塞模式中。
- 返回值-1,error=EINTR或EAGAIN以外的值,表示更严重的错误。
读取所有字节
ssize_t ret;
while(len != 0 && (ret = read(fd, buf, len)) != 0){
if(ret == -1){
if(errno == EINTR) continue;
perror("read");
break;
}
len -= ret;
buf +=ret;
}
/*
程序循环会从fd的当前文件位置读取len个字节到buf。它会继续读完len个字节或直接抵达EOF。
如果所读取的字节数目大于0,但小于len,则len的值会减去已读取的字节数目,
buf的值会加上已读取的字节数目,并且再次调用;
如果返回-1,errno=EINTR,则不需要更新参数就可以再次进行此调用;
如果返回-1,errno设定为其他值,则会调用perror()说明相关错误,终止循环。
*/