本章介绍关于文件I/O的系统调用。本章描述的函数都是不带缓冲的I/O,不带缓冲指的是每个read和write都调用内核中的一个系统调用。
1. 文件描述符fd
通常是一个非负整数,内核用它以标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,都返回一个文件描述符
标准输入 标准输出 标准错误:
每当运行一个新程序时,所有的shell都为其打开3个文件描述符,即标准输入、标准输出和标准错误。这三个描述符都链接至终端。
在头文件#include <unistd.h>
中,定义了三个常量以表示标准输入、标准输出、标准错误。
/* Standard file descriptors. */
#define STDIN_FILENO 0 /* Standard input. */
#define STDOUT_FILENO 1 /* Standard output. */
#define STDERR_FILENO 2 /* Standard error output. */
2. 函数open和openat
用来打开或创建一个文件,返回文件描述符
int open(const char *pathname, int flags[, mode_t mode]);
int openat(int dirfd, const char *pathname, int flags[, mode_t mode]);
仅当创建新文件时才使用最后一个参数。
-
参数pathname:要打开或要创建的文件名字
对于openat函数有些不同:
- pathname指定的是绝对路径名,此时fd参数被忽略
- pathname指定的是相对路径名,此时fd即为相对路径的开始地址,fd是通过打开目录文件获取的。
- pathname指定的是相对路径名,fd参数是特殊值
AT_FDCWD
,相对路径名以当前进程的工作目录开始。
-
参数flags:
- O_RDONLY:只读打开
- O_WRONLY:只写打开
- O_RDWR:读、写打开
- O_EXEC:只执行打开
以上几个常量必须且只能指定一个,可以与下面的常量进行或运算
- O_APPEND:每次写都追加到文件末尾
- O_CLOEXEC:将FD_CLOEXEC设置为文件描述符标志
- O_CREAT:此文件不存在时创建它,此时最后一个参数mode指定该文件的访问权限位
- O_DIRECTORY:指明pathname引用的是目录。如果不是目录则出错
- O_EXCL:如果同时制定了O_CREAT,而文件已经存在则出错。
- O_NOFOLLOW:如果pathname引用的是一个符号链接则出错
- O_NONBLOCK:如果pathname引用的是一个FIFO、块特殊文件或字符特殊文件,则此选项为此文件的本次打开操作和后续的I/O操作设置非阻塞方式。
- O_SYNC:使每次write等待物理I/O操作完成,包括由write操作引起的文件属性更新所需的I/O。
- O_TRUNC:如果文件存在,且只写或读写打开,则将其长度截断为0。
- O_DSYNC:每次write都等待物理I/O完成,但是如果写操作不影响读取刚写入的数据,则不等待文件属性更新
- O_RSYNC:每个以文件描述符作为参数的read操作等待,直到所有对文件同一部分的未决写操作完成
由open和openat函数返回的文件描述符fd一定是最小的未使用文件描述符数值(比如在程序中close了文件描述符1,那么新open一个文件,返回的fd就是最小未使用的文件描述符1。)。
3. 函数creat
创建一个新文件,返回文件描述符
int creat(const char *pathname, mode_t mode);
此函数等效于
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
creat函数的不足之处是只能以只写方式打开创建的文件。因此如果要读的打开创建的文件,就需要先creat再close,再open。因此建议直接使用open函数创建文件。
open(path, O_RDWR | O_CREAT | O_TRUNC, mode);
4. close函数
关闭一个文件描述符引用的打开的文件
int close(int fd);
关闭一个文件时还会释放该进程加在该文件上的所有记录锁。
当一个进程终止时,内核会自动关闭它所有的打开文件。
5. lseek函数
为一个打开的文件设置偏移量(读写指针)
off_t lseek(int fd, off_t offset, int whence);
- 参数offset:偏移量,大于0向文件尾偏移,小于0向文件头偏移
- whence:偏移开始位置
- SEEK_SET:从文件开头开始偏移
- SEEK_CUR:从当前值开始偏移
- SEEK_END:从文件尾开始偏移
函数成功返回新的文件偏移量。
lseek系统调用仅仅将当前文件偏移量记录在内核中,不引起任何I/O操作。这个偏移量用于之后的读写操作。
文件空洞:
文件偏移量可以大于文件长度,这时对该文件的下一次写操作会加长该文件,并在文件中构成一个空洞。空洞不要求在磁盘上占用存储区。
如果 offset 比文件的当前长度更大,下一个写操作就