[Linux] 多进程、多线程 同时写文件是否存在竞争

Linux系统通过 文件描述符表,文件表项,inode节点 这三大要素来管理文件操作。

 

文件描述符表:

文件描述符表是进程级别的,每个进程都有自己的文件描述符表,除了 0 ,1 , 2 这三个特殊的文件描述符外,不同进程文件描述符表中的节点代表不同的文件。比如有文件 /home/file ,在进程1中与之对应的描述符是20,但是在进程2中与之对应的描述符可能是50。但是不论是进程1的20还是进程2的50都是指向文件 /home/file的。

每个进程都有一个文件描述符表。

 

文件表项:

上面说到了不同进程的不同文件描述符可以指向同一个物理文件,那么不同进程是怎么互相协调的呢,如果进程1对文件只有读权限,进程2对文件有读写权限,那么这个权限信息如果写在同一个为位置,那么不就乱套了。所以需要一个中间隔离地带,这就是文件表项,每个进程的文件描述符表的每个节点都包含一个文件表项信息(实际上是有一个指向文件表项信息的指针)。

每个进程都有一个文件描述符表。每个文件描述符表的节点都指向一个文件表项。

文件表项中放什么信息呢? 其中有 文件操作权限, “当前进程” 对此文件的操作偏移量,注意引号中的内容,也就是说每个进程对某个物理文件都有自己的偏移量信息。比如进程1写到了距离文件开始100字节的位置,进程2写到了距离文件开始200字节的位置,那么这两个进程是互相不干扰的。进程1再写50字节,那么数据在文件的表现就是从 100~150字节,进程2写100字节,那么在文件中的表现就是 200~300字节被写入。

 

那么问题来了,每个进程对于物理文件都有自己偏移量,那么如果想让多进程写同一个文件,且都追加到尾部怎么办???最常见的例子就是日志系统,如果处理的不好,多进程写同一个文件最容易出现日志交叉。

这里要提到1个前提条件:所有的系统调用都是原子的,这个原子性即针对线程,也针对进程。

 

多进程场景下:

我们可以先通过lseek先把偏移量调整到 “当前时间点” 的文件尾部,然后在write写入数据。乍一看好像没问题,但是lseek 和 write是两个系统调用,因此无法保证 进程级和线程级 原子性。

这里就需要提到 O_APPEND 选项,我们在打开文件的时候可以声明此属性,这样每次 write 操作都会先 把偏移量调整到 当前尾部,然后写入数据。

因此,如果指定了O_APPEND,数据会完全有序地写入文件。

如果不指定而使用lseek+write的方法,那么可能会出现数据踩踏覆盖,而且会出现顺序颠倒,因为不同进程对于同一个文件会有不同的文件表项,也就是说这个时候会有两个偏移量值,每个进程管理自己的偏移量值。且这个偏移量值无法手工同步。

 

多线程场景下:

如果多线程操作同一个文件会怎样???

如果指定了O_APPEND,同样不需要手动管理。

如果不指定,因为对于同一个文件描述符在进程内部多线程是共享的,所有这些线程共享偏移量,因此不会出现数据踩踏覆盖,但是会出现数据颠倒,因为无法保证线程的调度顺序。

 

inode节点:

inode节点可以理解为内核文件系统维护的实际的物理文件内容,不太需要我们关心,内核会维护他的操作原子性,这也就是上面提到的 “系统调用总是原子的”

 

 

 

 

参考:https://blog.csdn.net/qq_28114615/article/details/94590598

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值