多个进程对同一文件写入的问题

2023-05-16

转载。

讨论关于并发环境下,多个进程对同一文件写入的问题,我们会涉及到文件共享的知识。在开始之前,我们先讨论一些有关文件共享的知识。

1. 文件共享

  Unix系统支持在不同进程间共享打开的文件。为此,我们先介绍一下内核用于所有I/O的数据结构。注意,下面的说明是概念性的,与特定的实现可能匹配,也可能不匹配。

  内核使用三种数据结构表示打开的文件,它们之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响。

  (1) 每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:

      (a) 文件描述符标识(close_on_exec)。

     (b)指向一个文件表项的指针。

  (2)内核为所有的打开文件维持一张文件表。每个文件表项包含:

      (a)文件状态标志(读、写、添加、同步和非阻塞等)。

      (b)当前文件偏移量。

      (c)指向该文件v节点的指针。

   (3)每个打开文件(或设备)都有一个v节点(v-node)结构。v节点包含了文件类型和对此文件进行各种操作的函数的指针。对于大多数文件,v节点还包含了该文件的i节点(i-node,索引节点)。这些信息是在打开文件时从磁盘上读入内存的,所以所有关于文件的信息都是快速可供使用的。例如,i节点包含了文件的所有者,文件长度,文件所在的设备,指向文件实际数据块在磁盘上所在位置的指针等等。

    注意:Linux没有使用v节点,而是使用了通用i节点结构。虽然两种实现有所不同,但在概念上,v节点与i节点是一样的。两者都指向文件系统特有的i节点结构。

    我们忽略了默写实现细节,但这并不影响我们的讨论。例如,打开文件描述符表可存放在用户控件,而非进程表中。这些表也可以用于多种方式的实现,不必一定是数组;例如,可将它们实现为结构的连接表。这些细节并不影响我们在文件共享方面的讨论。

    图1显示了一个进程的三张表之间的关系。该进程有两个不同的打开文件:一个文件打开为标注输入(文件描述符为0),另一个打开为标准输出(文件描述符为1)。从Unix系统的早期版本中[Thompson 1978]以来,这三张表之间的基本关系一直保持至今。这种安排对于在不同进程之间共享文件的方式非常重要。

                                        图1 打开文件的内核数据结构

  1. 注意:创建v节点结构的目的是对在一个计算机系统上的多文件系统类型提供支持。这一工作是有Peter Weihberger(贝尔实验室)和Bill Joy(Sun公司)分别独立完成的。Sun称此种文件系统为虚拟文件系统(Virtual File System),称与文件系统类型无关的i节点部分为v节点[Kleiman 1986].当哥哥制造商的实现增加了对Sun的网络文件系统(NFS)的支持时,它们都广泛采用了v节点结构。在BSD系统中首先提供v节点的是4.3BSD Reno版本,其中加入了NFS。

  2. 在SVR4中,v节点代换了SVR3中与文件系统类型无关的i节点结构。Solaris是从SVR4发展而来的,他也是用了v节点。

  3. Linux没有将相关的数据结构分为i节点和v节点,而是采用了一个独立于文件系统的i节点和一个依赖于文件系统的i节点。

     如果两个独立进程各自打开了同一个文件,则有图2中所示的安排。我们假设第一个进程在文件描述符3上打开该文件,而另一个进程则在文件描述4上打开该文件。打开该文件的每一个进程都得到一个文件表项,但对一个给定的文件只有一个v节点表项。每个进程都有自己的文件表项的一个理由是:这种安排使每一个进程都有它自己的对该文件的当前偏移量。

                                  图2 两个独立进程各自打开同一个文件 

    给出了这种数据结构后,现在对前面所描述的操作做进一步说明。   

  • 在完成每个write后,在文件表项中的当前文件偏移量即增加所写的字节数。如果当前文件偏移量超过了当前文件长度,则在i节点表项中的当前文件长度被设置为当前文件的偏移量。
  • 如果用O_APPEND标志打开了一个文件,则相应标志也被设置到文件表项的文件状态标志中。每次对这种具有添写标志的文件执行写操作时,在文件表项中的当前文件偏移量首先被设置为i节点表项中的文件长度。这就使得每次写的数据都添加到文件的当前尾端处。
  • 若一个文件用lseek定位到文件当前的尾端,则文件表项中的当前文件偏移量被设置为i节点表项中的当前文件长度。注意,这与用O_APPEND标志打开文件是不同的。
  • sleek函数只修改文件表项中的当前文件偏移量,没有进行任何文件I/O操作。

    可能有多个文件描述符指向同一个文件表项。在下一小节中讨论dup函数时,我们将能看见这一点。函数调用fork后产生的父子进程中,它们共享相同的i或v节点和同一个文件表项。(通过在现代Linux系统中进行测试得到的,测试程序和结构见下文(更正:测试结果分析有误!已更正!))。

    注意,文件描述符标志和文件状态标志在作用域方面的区别,前者只用于一个进程的一个描述符,而后者适用于指向该给定文件表项的任何进程中的所有描述符。上面所述的一切对多个进程读同一个文件都能正确工作。每个进程都有它自己的文件表项,其中也有它自己的当前文件偏移量。但是,当多个进程写同一个文件时,则可能产生预期不到得结果。为了说明如何避免这种情况,我们需要理解原子操作的概念。

2. 原子操作

2.1 添写至一个文件

    考虑一个进程,它要将数据添加到一个文件尾端。早期的UNIX系统版本并不支持open的O_APPEND选项,所以程序被编写成下列形式:

  1. if (lseek(fd, 0L, 2) < 0) /* position to EOF */
  2.     err_sys("lseek error");
  3. if (write(fd, buf, 100) != 100) /* and write */
  4.     err_sys("write error");

    对单个进程而言,这段程序能正常工作,但若对多个进程同时使用这种方法将数据添加到同一文件,则会产生问题。(例如,若此程序由多个进程同时执行,各自将消息添加到一个日志文件中,就会产生这种情况。)

    假定有两个独立的进程A和B都对同一个文件进行操作,给个进程都已打开了该文件,但未使用O_APPEND标志。此时,各数据结构之间的关系如图2所示。每个进程都有自己的文件表项,但是共享一个v节点表项。假定进程A调用了lseek,它将进程A的该文件当前偏移量设置为1500字节(当前文件尾端处)。然后内核调度进程使进程B运行。进程B执行sleek,也将其对该文件的当前偏移量设置为1500字节(当前文件尾端处)。然后B调用write函数,它将B的该文件当前文件偏移量增值1600.引文该文件的长度已经增加了,所以内核对v节点中的当前文件长度更新为1600.然后,内核又进行进程切换使进程A恢复运行。当A调用write时,就从其当前文件偏移量(1500字节)处将数据写到文件中去。这样就代换了进程B刚写到该文件中的数据。

     问题出在逻辑操作“定位到文件尾端处,然后写”上,它使用了两个分开的函数调用。解决问题的方法是使这两个操作对于其他进程而言成为一个原子操作。任何一个需要多个函数调用的操作都不可能是原子操作,因为在两个函数调用之间,内核有可能会临时挂起该进程。

     UNIX系统提供了一种方法是这种操作成为原子操作,该方法是在打开文件时设置O_APPEND标志。正如前面所述,这就是内核每次对这种文件进行写之前,都将进程的当前偏移量设置到文件的尾端处,于是在每次写之前就不在需要调用sleek了。

2.2 pread和pwrite函数

     Single UNIX Specification包括了XSI扩展,该扩展允许原子性地定位搜索(seek)和执行I(/O。pread和pwrite就是这种扩展。

  1. #include <unistd.h>

  2. ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
  3.                                         返回值:读到的字节数,若已到文件结尾则返回0,若出现错误返回-1
  4. ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t offset);
  5.                                         返回值:若成功则返回已写的字节数,若出错则返回-1

    调用pread相当于顺序调用lseek和read,但是pread又与这种顺序调用有下列重要区别:

  • 调用pread时,无法中断其定位和读操作。
  • 不更新文件指针。

    调用pwrite相当于顺序调用lseek和write,但也和它们有类似的区别。

2.3 创建一个文件

    在对open函数的O_CREAT和O_EXCL选项进行说明时,我们已经见到另一个有关原子操作的例子。当同时指定这两个选项,而该文件又已经存在时,open将失败。我们曾提及检查该文件是否存在以及创建该文件这两个操作是作为一个原子操作执行的。如果没有这样一个原子操作,那么可能会编写下面的程序段:

  1. if ((fd = open(pathname, O_WRONLY)) < 0) {
  2.     if (errno == ENOENT) {
  3.          if ((fd = creat(pathname, mode)) < 0)
  4.               err_sys("create error");
  5.     } else {
  6.          err_sys("open error");
  7.     }
  8. }

     如果在open和creat之间,另一个进城创建了该文件,那么就会引起问题。例如,若在这两个函数调用之间。另一个进程创建了该文件,并且写进了一些数据,然后,原先的进程执行这段程序中的creat,这时,刚由另一个进程写上去的数据就会被擦除掉。如若将这两者合并在一个原子操作中,这种问题就不会存在了。

     一般而言,原子操作(atomic operation)指的是由多步组成的操作,如果该操作原子地执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。

3. dup和dup2函数

     下面两个函数都可用来复制一个现存的文件描述符:

  1. #include <unistd.h>

  2. int dup(int filedes);
  3. int dup2(int filedes, int filedes2);
  4.                      两函数的返回值:若成功则返回新的文件描述符,若出错则返回-1

      由dup返回的新文件符一定是当前可用文件描述符中的最小数值。用dup2则可以用filedes2参数指定新描述符的数值。如果filedes2已经打开,则先将其关闭。如若filedes等于filedes2,则dup2返回filedes2,而不关闭它。

      这些函数返回的新文件描述符与参数参数filesdes共享同一个文件表项。如图3所示。

                                   图3 执行dup之后的内核数据结构

    在图3中,我们假定进程执行了:

  1. newfd = dup(1);

    当此函数开始执行时,假定下一个可用的文件描述是3(这是非常有可能的,因为0、1、2是由shell打开的)。因为两个描述符指向同一文件表项,所以它们共享同一个文件状态标志(读、写、添加等)以及同一文件当前偏移量。

    每个文件描述符都有它自己的一套文件描述符标志。新描述的执行时关闭(close-on-exec)标志总是由dup函数清除。

    复制一个描述符的另一种方法是使用fcntl函数,实际上,调用

  1. dup(filedes);

等效于

  1. dup2(filedes, F_DUPFD, 0);

而调用

  1. dup2(filedes, filedes2);

等效于

  1. close(filedes2);
  2. fcntl(filedes, F_DUPFD, filedes2);

在后一种情况下,dup2并不完全等同于close()加上fcntl.它们之间的区别是:

  • dup2是一个原子操作,而close及fcntl则包含两个函数调用。有可能在close和fcntl之间插入执行信号捕获函数,它可能修改文件描述符。
  • dup2和fcntl有某些不同的errno。

4. 测试结果  

    带有O_APPEND标志的测试代码和结果数据如下:  o_append_text.rar   

    没有带O_APPEND标志的测试代码和结果数据如下: test.rar   

    可以看到,o_append_text.rar里面的测试结果数据文件大小是test.rar里面的2倍。分析其原因,是因为函数调用fork时,在父子进程中没有为他们采取必要的同步措施,因此在写文件时有竞争发生,导致结果混乱。另外,文件大小的不同是因为,文件表项中的文件偏移量的值类型并不是一个易失形变量类型,从而导致在写文件时读取的偏移值变量的值不是最新的值,从而导致文件大小会不同的结果。

    另外,从结果数据中(可能数据没有充分表现出如下所说的情况,但是你可以通过调整测试程序里的参数,并多运行几次测试程序就可以得到如下所述的结果)可以得出:当以O_APPEND标志打开文件时,write将执行原子操作,read亦然。而没有使用O_APPEND标志打开文件时,父子进程的数据输出将出现乱序的情况。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多个进程对同一文件写入的问题 的相关文章

  • C++11带来的move语义

    C 43 43 11带来了move语义 xff0c 可以有效的提高STL的效率 xff0c 这篇文章写的非常好 xff0c 可以参考 xff0c 这里对原文进行翻译 xff0c 加入我自己的理解 原文 xff1a http www cpro
  • C++11带来的lambda表达式

    C 43 43 11带来了lambda表达式 xff0c 可以简化程序的编写 xff0c 使代码更加清晰 现在按照步骤来介绍lambda表达式 xff1a 1 函数对象 又叫仿函数 xff0c 如果一个类或者结构体重载了operator 操
  • caffe中几个基本概念

    caffe中几个基本概念 1 caffe中的blob结构是用来进行数据存储 交换和处理网络中正向反向迭代时的数据和导数信息的数据结构 blob是caffe的标准数组结构 他提供了一个统一的内存接口 其将内部的cpu gpu数据之间的传输与存
  • 摄像头引脚定义

    摄像头引脚定义 1 NC NO CONNECT 2 AGND Power Analog ground 3 SIO D I O SCCB serial interface data I O 4 AVDD Power Analog power
  • Android7.0 JACK编译器不支持多用户同时编译的问题的解决

    xfeff xfeff Android7 0 xff08 也就是Android N xff09 上默认使用JACK编译器而不再使用openjdk了 xff0c 但发现JACK不是很好用 xff0c 比如最大的一个问题就是 xff0c 同一台
  • 【树莓派】死机自动重启、掉线自动重连

    目录 WIFI掉线自动重连 首先查看你的板子硬件型号 拿树莓派去做服务器就要配置下这两项 xff0c 保证随时能够VNC控制 WIFI掉线自动重连 http shumeipai nxez com 2017 01 25 raspberry p
  • open vswitch分析

    Open vSwitch 概述 Open vSwitch xff08 下面简称 OVS xff09 是一个高质量的 多层虚拟交换机 OVS 遵循开源 Apache2 0 许可 xff0c 通过可编程扩展 xff0c OVS 可以实现大规模网
  • C# 接口《通俗解释》

    原文地址 xff1a https www cnblogs com hamburger p 4681681 html 接口的定义 xff1a 接口是指定一组函数成员 xff0c 而不实现他们的引用类型 接口使用interface 关键字进行定
  • linux 如何查看指定动态库

    要查看 Linux 系统指定的动态库 xff0c 可以使用以下命令 xff1a 使用 ldconfig 命令 xff1a ldconfig p 该命令将显示系统已加载的所有动态库及其路径 如果要查找特定动态库 xff0c 可以使用 grep
  • Tortoisegit 恢复文件夹被删除的文件(被误删)

    关于Tortoisegit 恢复git文件夹中被删除的文件 xff1a 1 在git文件夹右键tortorisegit show log 2 选择版本 xff08 当时执行删除操作的版本 xff09 3 选择被delete掉的 xff0c
  • putty screen 快捷键

    使用putty的时候 xff0c 开启screen再detach xff0c 可以防止跑程序过程中断开连接而导致程序中断 总结了下putty与screen 相关的快捷键 目前常用的有如下几个 xff08 命令均在putty终端输入 xff0
  • Magento的不同版本(CE,EE,ECE)介绍

    Magento提供了三个不同的版本平台 xff0c 即Magento Community Edition xff08 CE xff09 社区版 xff0c Magento Enterprise Edition xff08 EE xff09
  • c语言初学,字母大小写转换

    这类题目主要通过ASCII码差值实现 xff0c A对应ASCII码十进制数字是65 xff0c a对应ASCII码十进制数字是97 xff0c 即大小写字母之间ASCII码差值为32 xff0c 想要将大写字母转换为小写字母可以将该字符A
  • matlab——subplot多子图共用一个colorbar,微调子图和colorbar位置

    用subplot命令画出多个图后 xff0c 需要让这些图共用一个colorbar 在这里与大家分享我的操作 xff0c 希望能帮助到有需要的人 备注 xff1a 从 R2019b 开始 xff0c 可以在分块图布局中显示共享颜色栏 xff
  • 远程连接服务器数据库报错:Host ‘XXXXXX’ is blocked because of many connection errors

    一 我遇到的问题描述 使用Navicat for mysql连接公司的服务器数据库 xff0c 报错 xff1a Host XXXXXX is blocked because of many connection errors 二 出现错误
  • android中MediaCodec硬编码中关键帧间隔时间设置问题

    在MediaCodec硬编码中设置 xff29 关键帧时间间隔 xff0c 在 xff21 xff30 xff29 中是这么设置的 mMediaCodec 61 MediaCodec createByCodecName debugger g
  • python3 网络编程问题——虚拟机centos7上运行tcp服务器,在主机win10上使用网络调试助手作为tcp客户端无法建立连接,提示1035错误:the socket is marked...

    前提 xff1a 主机和虚拟机都是在同一网段下 我的网络调试助手的连接结果如下图 xff1a 注意 红框中的提示 xff0c 连接超时的结果可能是由于以下两种可能的情况导致的 xff1a 1 服务器端口未开启监听 2 路由项被防火墙拦截 对
  • DSSM pytorch实现

    之前在网上找到了一个文本匹配实现仓库 xff0c 但是没有提供DSSM的代码 xff0c 我就根据那个代码实现以下DSSM 数据集采用的是蚂蚁金服的数据集 也参考过别人的代码 xff0c 但是总感觉怪怪的 xff0c DSSM原文中 xff
  • 文本匹配实验结果总结

    主要把一些实验结果说一下 xff1a DSSM 作为文本匹配的开山鼻祖 xff0c 想法也很简单 xff0c 就是将query 和doc拉到同一维度 xff0c 然后计算余弦相似度 xff0c 网络也是非常简单 xff0c 所以只获得了78
  • 使用wikiextractor 提取wiki数据

    wikiextractor包链接地址 xff1a https github com attardi wikiextractor 安装wikiextractor pip install wikiextractor 然后下载wiki语料库 xf

随机推荐

  • python爬虫(Python读取TXT文件中的URL并下载文件)

    前言 xff1a 本人之前并没有接触过python爬虫 xff0c 但是现在因为要做个试验 xff0c 需要下载海量人脸图片 xff0c 所以需要用到python爬虫这个办法 但是过程中遇到到了很多问题 xff0c 程序调了很久都不成功 x
  • 机器学习之python读取CSV文件

    当我们在用python处理机器学习的问题时 xff0c 往往需要先读取数据 xff0c 这些数据通常都是文件 xff0c 我今天遇到的是CSV文件 xff0c 是在kaggle竞赛数据集下载的 xff08 比如手写数字识别 xff0c 以及
  • Robust PCA Low-rank(附matalb 代码)

    最近在看的论文中 xff0c 包括人脸识别 xff0c 以及深度神经网络模型压缩等论文中 xff0c 都会有low rank 低秩 低秩稀疏分解等解决方法 xff0c 感觉关于low rank的研究还挺火的 xff0c 这个问题和Robus
  • 完美图解教程 Linux环境VNC服务安装、配置与使用

    图片直观明了 xff0c 尝试一步一步分享俺的成果 1 xff1a 下面第一步当然是确认自己linux系统是否安装VNC 默认情况下 xff0c Red Hat Enterprise Linux安装程序会将VNC服务安装在系统上 打开终端窗
  • torch.ge,torch.gt,torch.le

    torch ge torch ge input other out 61 None Tensor 逐元素比较input和other xff0c 即是否 input gt 61 otherinput gt 61 other 如果两个张量有相同
  • VAE(变分自动编码器)

    首先先贴上一些有用的链接 xff1a https www cnblogs com king lps p 8477300 html https zhuanlan zhihu com p 29685634 再补充一个链接 xff1a https
  • 视频去噪资料整理(深度学习方法)

    近几天做了一些视频去噪的一些边缘工作 xff0c 故整理一下 我读的一篇论文 xff1a paper TOFlow Video Enhancement with Task Oriented Flow code https github co
  • 正则表达式里的?:是啥意思例如(?:a|b)

    我也是找答案时看到的 xff0c 发现这里回答的理论是的 xff0c 但不好让人理解 xff0c 我也结合其他地方的资料 xff0c 才能理解他的答案 下面我用两个例子来说明 单引号里面的三个元字符之间的意思 39 39 1 39 39 x
  • idea设置javadoc、idea2020单行注释的快捷键

    找了半天 xff0c 想不起来叫什么 1 其实就是给方法和类上添加这种doc文档的注释的快捷键 xff1a 打开setting keymap 搜索 xff1a fix doc comment 我习惯用ctrl 43 enter 2 切换到i
  • LaTeX分享008【LaTeX多图排列方法】

    LaTeX分享 LaTeX多图排列方法 作者 xff1a JinyuLi 日期 xff1a 2023 03 18 内容 xff1a 填坑 xff0c 补充上一篇专栏中LaTeX图片插入问题中的多张图片同时插入的实现方法部分 xff0c 本篇
  • SONiC(2):手动运行sonic-vs

    SONiC的testbed都是用ansible自动部署的 xff0c 下面尝试手动来创建 另外最新的sonic vs可以从这里下载 准备工作 ansible生成的vlab 01这个虚机的vir配置xml文件如下 testbed 64 u18
  • SONiC vs testbed搭建

    准备工作 一台安装Ubuntu18 04的系统 xff0c 内存建议不少于16G 需要支持kvm虚拟化安装ssh server sudo apt update y sudo apt openssh server y 设置sudo免密 sud
  • 2. 安装GNOME和KDE图形化桌面

    安装GNOME和KDE图形化桌面 1 使用CD DVD介质配置本地Yum源2 安装GNOME图形化桌面2 1 安装GNOME xff1a 2 2 设置在系统启动时进入图形化桌面3 安装KDE图形化桌面3 1 安装KDE xff1a 3 2
  • 复位电路的几种设计

    本人转自 xff1a http hi baidu com yinweini2 item 48ba4f12f54587711009b591 复位源是导致单片机内部复位操作的源泉 xff0c 大致可分为七种 xff1a 上电复位 xff08 P
  • Hadoop入门经典:WordCount

    以下程序在hadoop1 2 1上测试成功 本例先将源代码呈现 xff0c 然后详细说明执行步骤 xff0c 最后对源代码及执行过程进行分析 一 源代码 package org jediael hadoopdemo wordcount im
  • Jlink 采用 SWD 模式下载电路接法

    在Jlink上 xff1a 对应的电路图为 xff1a 其中要使用的是 Pin1 gt vcc Pin7 gt SWDIO Pin9 gt SWCLK Pin4 gt GND 其他GND 引脚也可 在对于的开发板 xff08 举例 xff1
  • ubuntu系统硬盘温度过高的解决方法

    一 更改swap分区设置 在ubuntu 里面 xff0c swappiness的值的大小对如何使用swap分区是有着很大的联系的 swappiness 61 0的时候表示最大限度使用物理内存 xff0c 然后才是 swap空间 xff0c
  • MBus协议详解(一)

    看了许多关于MBus协议的资料 xff0c 感觉说的不具体 不完整 也不系统 xff0c 本人准备结合一个具体的产品实现 xff0c 从理论和实现上对MBus协议做一个详细的论述 xff0c 如有不当之处 xff0c 欢迎讨论 1 介绍 M
  • MBus协议详解(二)

    4 4 slave 设计 传输特性 xff1a slaves 被设计为具有两种不同恒定 sink 电流 xff0c 因此在总线上电压有 1V 的变化的时候 xff0c sink 电流的变化一定不能超过 0 2 为了传输一个 Mark xff
  • 多个进程对同一文件写入的问题

    转载 讨论关于并发环境下 xff0c 多个进程对同一文件写入的问题 xff0c 我们会涉及到文件共享的知识 在开始之前 xff0c 我们先讨论一些有关文件共享的知识 1 文件共享 Unix系统支持在不同进程间共享打开的文件 为此 xff0c