3. 文件I/O的内核数据结构
(1) 内核数据结构表
数据结构 |
主要成员 |
文件描述符表 |
①文件描述符标志 ②文件表项指针 |
文件表项 |
①文件状态标志(读、写、追加、同步和非阻塞等状态标志) ②当前文件偏移量 ③i节点表项指针 ④引用计数器 |
i节点 |
①文件类型和对该文件的操作函数指针 ②当前文件长度 ③文件所有者 ④文件所在设备、文件访问权限 ⑤指向文件数据在磁盘块上所在位置的指针等。 |
(2)3张表的关系
4. 文件的原子操作
(1)文件追加
①打开文件时使用O_APPEND标志,进程对文件偏移量调整和数据追加成为原子操作。相当于write函数将以下3个操作作为一个原子操作,不可被打断。
A.从i节点读取文件长度作为当前偏移量
B.往文件中写入数据
C.修改i节点中文件长度信息等
②内核每次对文件写之前,都将进程的当前偏移量设置为该文件的尾端。这样不再需要lseek来调整偏移量。
(2)文件创建
对open函数的O_CREAT和O_EXCL的使用,而该文件存,open将失败,否则创建该文件,并且使得文件是否存在的判定和创建过程成为原子操作。
【编程实验】原子操作
//file_append.c
#include <unistd.h> #include <stdio.h> #include <stdlib.h> //exit #include <string.h> //strlen #include <fcntl.h> //O_WRONLY //为了演示文件定位与追加数据是否被打断,需要打开该程序的两个进程,分别按 //文件的格式在命令行中输入相应的参数。 int main(int argc, char* argv[]){ if( argc < 3){ printf("usage: %s content destfile ", argv[0]); exit(1); } //注意,Linux默认下文件锁是建议锁(具体见后面“文件锁”方面的内容),所以 //尽管加了O_WRONLY,但不同进程仍然可以同时修改这个文件) //int fd = open(argv[2], O_WRONLY); //这种方式的定位与追加不是原子操作的 int fd = open(argv[2], O_WRONLY | O_APPEND); //定位与追加是原子操作 if(fd < 0){ perror("open error"); exit(1); } //定位到文件尾部(只使用O_WRONLY选项时,需手动定位到文件末尾 //lseek(fd, 0L, SEEK_END); sleep(10); //为了把定位与写入过程隔开,以便演示多进程同时写入同一文件 //时会出现后启动进程格覆盖之前进程写过的内容。 //往文件尾部追加内容 size_t size = strlen(argv[1])*sizeof(char); if(write(fd, argv[1], size)!=size){ perror("write error"); exit(1); } close(fd); return 0; }