1 文件描述符
文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。将其作为参数传送给 read 或 write等操作。
UNIX 系统 shell 使用文件描述符 0 与进程的标准输入相关联,文件描述符 1 与进程的标准输出相关联,文件描述符 2 与进程的标准出错输出相关联。
2 不带缓冲的 I/O 函数
术语不带缓冲指的是每个 read 或 write 都调用内核中的一个系统调用,即user态不存在缓冲区存在。
2.1 open 函数
#include <fcntl.h> int open(const char *pathname, int oflag, ... /* mode_t mode */);
对于 open 函数而言,仅当创建新文件时才使用第三个参数。
pathname 是要打开或创建文件的名字。
oflag 参数用于说明此函数的多个选项。用下列一个或多个常量进行“或”运算构成 oflag 参数(这些常量定义在 <fcntl.h> 头文件中)。
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读、写打开
这三个常量中必须指定一个且只能指定一个。下列常量则是可选的:
O_APPEND 每次写时都追加到文件的尾端。
O_CREAT 若文件不存在,则创建它。使用此选项时,需要第三个参数 mode,用于指定该新文件的访问权限位。
O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则会出错。用此可以测试一个文件是否存在,如果不存在,则创建此文件。
O_TRUNC 如果此文件存在,而且为只写或者读写成功打开,则将其长度截短为 0.
O_NOCTTY 如果 pathname 指的是终端设备,则不将该设备分配为此进程的控制终端。
O_NONBLOCK 如果指的是一个 FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本地打开操作和后续的 I / O 操作设置非阻塞模式。
mode 参数,当指定了O_CREAT 选项时起作用
第三个参数取下面列表中的这些常量的“或”运算结果用于指定新文件的访问权限(这些常量定义在 <sys/stat.h>中)。
S_IRUSR | 用户 -读 |
S_IWUSR | 用户 -写 |
S_IXUSR | 用户 -执行 |
S_IRGRP | 组 -读 |
S_IWGRP | 组 -写 |
S_IXGRP | 组 -执行 |
S_IROTH | 其他 -读 |
S_IWOTH | 其他 -写 |
S_IXOTH | 其他 -执行 |
open 函数的 oflag 参数还支持三个可选常量,用来解决多线程之间的read,write同步操作
O_DSYNC 使每次 write 等待物理 I/O 操作完成,但是,如果写操作并不影响读取刚写入的数据,则不等待文件属性被更新。
O_RSYNC 使每一个以文件描述符作为参数的 read 操作等待,直至任何对文件同一部分进行的未决写操作都完成。
O_SYNC 使每次 write 都等待物理 I/O 操作完成,包括由 write 操作引起的文件属性更新所需的I/O。
当文件用O_DSYNC 标志打开时,在重写其现有的部分内容时,文件时间属性不会同步更新。与此相反,如果文件是用O_SYNC 标志打开时,那么对该文件的每一次 write 操作都将在 write 返回之前更新文件时间。
2.2 close 函数
当一个进程终止时,内核自动关闭它所有打开的文件。
2.3 read 函数
调用 read 函数从打开的文件中读数据。
2.4 write 函数
调用 write 函数想打开的文件写数据。
对于32位系统来说,每一个块大小是4K,因此当一次写入数据大小是4K的倍数时,理论上写入操作花费时间最短。如果每次写入小于4K,写入内容越少,与内核交互越多(user态到kernel态),花费时间越多!
2.5 lseek 函数
调用 lseek 显示地为一个打开的文件设置其偏移量。
• 若 whence 是SEEK_SET,则将该文件的偏移量设置为距离文件开始处offset 个字节。
• 若 whence 是SEEK_CUR,则将该文件的偏移量设置为其当前值加 offset,offset 可为正或负。
• 若 whence 是SEEK_END,则将该文件的偏移量设置为文件长度家 offset,offset 可为正或负。
管道、FIFO 和网络套接字都不支持设置偏移量,如果一个文件描述符引用的是这三者之一,则 lseek 函数返回 -1,并将 errno 设置为 ESPIPE。
文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。位于文件中但没有写过的字节都被读为 0.
2.6 pwrite,pread 函数
原子操作,避免出现位置定位偏移。
ssize_t pread(int fd,void *buf,size_t nbytes,off_t offset); ssize_t write(int fd,const void *buf,size_t nbytes,off_t offset);相当于执行lseek+read/write 操作,避免分开执行造成进程间定位不准确,出现问题。
注意:执行之后,文件偏移地址不发生改变!
2.7 dup,dup2 函数
复制一个文件描述符。
#include<unistd.h>
int dup(int fd);
int dup2(int fd1,int fd2)
2.7 sync,fsync,fdatasync函数
当向文件写入数据时,内核会先复制到缓冲区,然后排入队列,之后写入磁盘。
sync: void sync(void)将所有修改过的块缓冲区排入队列(不等待写入磁盘)
fsync:int fsync(int fd) 将fd修改排入队列,写入磁盘后返回,并更新文件属性
fdatasync:int fdatasync(int fd)与fsync类似,但是不更新文件属性