四、文件IO——内核数据结构和原子操作
2018-05-13 20:24
666 查看
4.1 缓存 buff 说明
一般设置缓存 buff 的大小是由一定的规律的,就是根据磁盘块的大小来定。Linux下输入命令: df -k 查看磁盘
可以用命令查看下 /dev/sda1 磁盘的磁盘说明
1 sudo tune2fs -l /dev/sda1
Block size 就是磁盘块的大小,这个磁盘块的大小为 4M ,那么就可以设置缓存 buff 大小为 4096,一次就可以将数据写入。
设置的缓存大小最好与磁盘块的大小保持一致,有利于提升读写文件的效率。
4.2 操作文件中内核数据结构简要介绍
一个打开的文件再内核中使用三种数据结构表示文件描述符
文件描述符标志
文件表项指针
文件表项
文件状态标志
读、写、追加、同步和非阻塞等状态标志
当前文件偏移量
i 节点表项指针
引用计数器
i 节点
文件类型和对该文件的操作函数指针
当前文件长度
文件所有者
文件所在的设备、文件访问权限
指向文件数据在磁盘上所在位置的指针等
4.3 原子操作
4.3.1 介绍
主要是open 函数中的文件追加和文件创建文件追加
打开文件时,使用 O_APPEND 标志,进程对到文件偏移量调整和数据追加成为原子操作
内核每次对文件写之前,都将进程的当前偏移量设置为该文件的尾端。这样不再需要 lseek 来调整偏移量
文件创建
对 open 函数的 O_CREAT 和 O_EXCL 的同时使用,而该文件存在,open 将失败,否则创建该文件,并且使得文件是否存在的判定和创建过程成为原子操作。
例子:两个进程对同一文件进行追加,没有使用 append 的时候
file_append.c
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <fcntl.h> 4 #include <unistd.h> 5 #include <string.h> 6 #include <errno.h> 7 #include <stdlib.h> 8 #include <stdio.h> 9 #include <fcntl.h> 10 11 int main(int argc, char *argv[]) 12 { 13 if(argc < 3) { 14 fprintf(stderr, "usage: %s content destfile\n", argv[0]); 15 exit(1); 16 } 17 18 int fd; 19 int ret; 20 size_t size; 21 22 fd = open(argv[2], O_WRONLY); 23 if(fd < 0){ 24 perror("open error"); 25 exit(1); 26 } 27 28 //定位到文件尾部 29 ret = lseek(fd, 0L, SEEK_END); 30 if(ret == -1) { 31 perror("lseek error"); 32 close(fd); 33 exit(1); 34 } 35 36 sleep(10); //睡眠 10s 37 38 //往文件中追加内容 39 size = strlen(argv[1]) * sizeof(char); 40 if(write(fd, argv[1], size) != size) { 41 perror("write error"); 42 close(fd); 43 exit(1); 44 } 45 46 return 0; 47 }
编译:gcc -o bin/file_append src/file_append.c
创建一个 append.txt 文件,然后开启两个终端运行此程序
第一个终端:
第二各终端:
第二个终端在第一个终端之后运行,运行完之后,查看 append.txt 的内容:
现象上说明,第二个终端的写入将第一个终端的写入给覆盖掉了。
第一个进程运行的时候,文件表项中的当前偏移量来源于 i 节点的文件长度(即调用 lseek 的时候),第二个进程运行的时候也是用 lseek 来获取偏移量,但是 i 节点中的文件长度没有增加,所以文件表项中的 当前偏移量 依然未变,因此第二个进程追加的内容覆盖掉了第一个进程中的内容。
要想不覆盖,则要使用原子操作。将 open 和 注释掉 lseek 的代码做修改:
1 //fd = open(argv[2], O_WRONLY); 2 fd = open(argv[2], O_WRONLY | O_APPEND);
删除 appent.txt 中的内容,然后再次在两个终端中运行两个程序:
加了 O_APPEND 后,write 函数做了几件事情,此时整个 write 成为一个原子操作,只有当第一个进程的 write 执行完后,第二个进程的 write 才后执行:
从 i 节点中读取文件长度作为当前偏移量
往文件中写入数据
修改 i 节点中文件操作
相关文章推荐
- 第3章 文件I/O(3)_内核数据结构、原子操作
- APUE文件IO总结,文件描述符、原子操作
- 文件IO中的原子操作
- 文件IO的原子操作
- 内核里操作文件
- 【讨论】文件操作的原子性
- 文件IO操作——流文件(字节流、字符流)小结
- .NET中的文件IO操作实例
- C#文件目录IO常见操作汇总
- 利用temp文件实现原子操作
- linux系统编程之文件与IO(八):文件描述符相关操作-dup,dup2,fcntl
- java.io常见流/java.io.file文件操作大全
- C语言文件IO操作
- 文件操作,及文件操作时的权限设置,快速实现文件拷贝,C语言常用IO函数
- java基础——文件(IO)操作2
- java.io.File类各种文件操作
- Linux C——带IO缓冲的文件操作函数
- java的io操作(将字符串写入到txt文件中)
- Java中的IO操作(文件读取的几种方式)
- C标准库函数--文件IO操作函数。