目录
文件内核数据结构
- 一个打开的文件在内核中使用三种数据结构表示
文件描述符表
- 文件描述符标志
- 文件表项指针
文件表项
- 文件状态标志
- 读、写、追加、同步和非阻塞等状态标志(例如open函数的flag参数)
- 当前文件偏移量(lseek函数改的就是这里)
- i节点表项指针
- 引用计数器(在多进程中应用)
i节点
- 文件类型和对该文件的操作函数指针
- 当前文件长度
- 文件所有者
- 文件所在的设备、文件访问权限
- 指向文件数据在磁盘块上所在位置的指针等
原子操作
文件追加
- 打开文件时使用O_APPEND标志,进程对文件偏移量调整和数据追加成为原子操作。
- 内核每次对文件写之前,都将进程的当前偏移量设置为该文件的尾端。这样不再需要lseek来调整偏移量。
文件创建
- 对open函数的O_CREAT和O_EXCL的使用,而该文件存在,open将失败,否则创建该文件,并且使得文件是否存在的判定和创建过程成为原子操作。
- 案例1:不使用O_APPEND
- src/file_append
#include <fcntl.h> #include <unistd. h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> int main(int argc, char * argv[]) { if (argc !=3){ fprintf(stderr, "usage: %s srcfile destfile\n", argv[0]); exit(1); } int fd = open(argv[2],O_WRONLY); if(fd <0){ perror("open error"); exit(1); } //定位到文件尾部 lseek(fd, 0L, SEEK_END); sleep(10); //往文件尾部追加内容 size_t size = strlen (argv[1])* sizeof(char); if(write(fd, buffer, size)!= size){ perror("write error"); exit(1); } return 0; }
- 编译后,开启两个终端
- 由于两个进程不共享lseek的偏移量,后面的内容将前面的内容覆盖了。
- 案例2:使用O_APPEND
- src/file_append
#include <fcntl.h> #include <unistd. h> #include <errno.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> int main(int argc, char * argv[]) { if (argc !=3){ fprintf(stderr, "usage: %s srcfile destfile\n", argv[0]); exit(1); } int fd = open(argv[2],O_WRONLY | O_APPEND); if(fd <0){ perror("open error"); exit(1); } sleep(10); //往文件尾部追加内容 size_t size = strlen (argv[1])* sizeof(char); if(write(fd, buffer, size)!= size){ perror("write error"); exit(1); } return 0; }
- 结果显示:
- 在open函数中加入O_APPEND标志
- write()函数(write是一个原子操作):
- 1)从 i 节点中读取文长度作为 i 偏移量
- 2)往文件中写入数据
- 3)修改 i 节点中文件长度
Linux程序设计:文件内核数据结构、原子操作及案例
最新推荐文章于 2022-07-15 17:40:18 发布