目录
文件内核数据结构
原子操作
文件内核数据结构
一个打开的文件在内核中使用三种数据结构表示
文件描述符表
文件描述符标志文件表项指针文件表项
文件状态标志
读、写、追加、同步和非阻塞等状态标志(例如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 节点中文件长度