Linux程序设计:文件内核数据结构、原子操作及案例

目录

文件内核数据结构

原子操作


文件内核数据结构

  • 一个打开的文件在内核中使用三种数据结构表示

文件描述符表
  • 文件描述符标志
  • 文件表项指针
文件表项
  • 文件状态标志
    • 读、写、追加、同步和非阻塞等状态标志(例如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 节点中文件长度

猜你喜欢

转载自blog.csdn.net/baidu_41388533/article/details/108402907