可以安全地让多个进程同时写入同一个文件吗?[CentOs 6,ext4]

Fix*_*xee 47 filesystems multiprocessing seek

我正在构建一个系统,其中多个从属进程通过unix域套接字进行通信,并且它们同时写入同一文件.我从未研究过文件系统或这个特定的文件系统(ext4),但感觉这里可能存在一些危险.

每个进程写入输出文件的不相交子集(即,正在写入的块中没有重叠).例如,P1仅写入文件的前50%,P2仅写入第二个50%.或者P1P2写入偶数块时可能只写入奇数块.

拥有P1P2(在不同的线程上同时运行)写入同一文件而不使用任何锁定是否安全?换句话说,文件系统是否隐式强加了某种锁定?

注意:遗憾的是,我不能随意输出多个文件并在以后加入.

注意:自发布此问题以来,我的阅读与下面仅发布的答案不一致.我读过的所有内容都表明我想做的事情很好,而下面的受访者坚持认为我所做的是不安全的,但我无法辨别所描述的危险.

jan*_*neb 29

如果您正在使用POSIX"原始"IO系统调用,例如read(),write(),lseek()等,那么您所做的事情似乎完全没问题.

如果你使用C stdio(fread(),fwrite()和朋友)或其他一些有自己的用户空间缓冲的语言运行库,那么"Tilo"的答案是相关的,因为缓冲,这是一些在您无法控制的范围内,不同的进程可能会覆盖彼此的数据.

Wrt OS锁定,而POSIX声明写入或读取小于PIPE_BUF的大小是某些特殊文件(管道和FIFO)的原子,对常规文件没有这样的保证.在实践中,我认为页面中的IO很可能是原子的,但是没有这样的保证.操作系统仅在内部锁定到保护其自身内部数据结构所必需的程度.可以使用文件锁或其他一些进程间通信机制来序列化对文件的访问.但是,所有这些只与您有几个进程在文件的同一区域执行IO有关.在您的情况下,由于您的进程正在执行IO以分离文件的各个部分,这些都不重要,您应该没问题.

  • 我使用 lseek() 读取 16*1024 的特定倍数,然后使用 write() 写入 16KiB 数据。进程**从不**与其写入区域重叠。 (3认同)

Til*_*ilo 27

不,一般来说这样做是不安全的!

您需要为每个进程获取一个独占的写锁定 - 这意味着所有其他进程必须等待一个进程写入文件...您拥有的I/O密集型进程越多,等待时间就越长.

最好每个进程有一个输出文件,并在行的开头用时间戳和进程标识符格式化这些文件,以便以后可以脱机合并和排序这些输出文件.

提示:检查Web服务器日志文件的文件格式 - 这些是使用行开头的时间戳完成的,因此可以稍后对它们进行组合和排序.


编辑

UNIX进程在打开文件(例如4096字节)时使用某个/固定的缓冲区大小,以便与磁盘上的文件进行数据传输.写入缓冲区已满后,进程将其刷新到磁盘 - 这意味着:它将完整的完整缓冲区写入磁盘!请注意,缓冲区已满时会发生这种情况! - 当有一个终点时!这意味着即使对于将面向行的文本数据写入文件的单个进程,这些行通常在刷新缓冲区时在中间某处切割.只有在最后,当文件在写入后关闭时,你能否认为该文件包含完整的行!

因此,根据您的进程何时决定刷新缓冲区,它们会在不同的时间写入文件 - 例如,顺序不是确定性/可预测的当缓冲区被刷新到文件时,您不能认为它只会写出完整的行 - - 例如,它通常会写出部分行,如果多个进程在没有同步的情况下刷新缓冲区,则会弄乱输出.

在维基百科上查看这篇文章:http://en.wikipedia.org/wiki/File_locking#File_locking_in_UNIX

引用:

Unix操作系统(包括Linux和Apple的Mac OS X,有时称为Darwin)通常不会自动锁定打开的文件或运行程序.不同类型的Unix提供了几种文件锁定机制,许多操作系统支持多种兼容性.两种最常见的机制是fcntl(2)和flock(2).第三种这样的机制是lockf(3),它可以是分开的,或者可以使用前两个原语中的任何一个来实现.

您应该使用flock或Mutexes来同步进程,并确保其中只有一个可以一次写入文件.

正如我之前提到的,为每个进程提供一个输出文件可能更快,更简单,更直接,然后在需要时(离线)组合这些文件.例如,某些Web服务器使用此方法,需要从多个线程记录多个文件 - 并且需要确保不同的线程都是高性能的(例如,不必在文件上等待彼此锁).


这是一个相关的帖子:(检查Mark Byer的答案!接受的答案不正确/相关.)

使用>>将几个并行进程的输出传输到一个文件是否安全?


编辑2:

在评论中,您说要将不同进程的固定大小的二进制数据块写入同一文件.

只有在块大小与系统文件缓冲区大小完全相同的情况下,这才有效!

确保固定块长度与系统的文件缓冲区大小完全相同.否则,您将遇到与未完成的行相同的情况.例如,如果您使用16k块,并且系统使用4k块,那么通常您会看到文件中的4k块看似随机顺序 - 无法保证您始终会看到来自同一进程的4个块

  • 等等,`fcntl`是a)建议,b)允许区域锁定,如果原始海报知道两个线程始终写入不相交的区域,这似乎是不必要的开销.(这假定文件已预先分配到已知的最终大小.) (3认同)
  • @Fixee:那么我最好的建议就是制作一个小玩具程序并尝试一下.**确保固定的块长度正好是系统的文件缓冲区大小.**否则,您将遇到与未完成的行相同的情况.例如,如果您使用16k块,并且系统使用4k块,那么通常您将以随机顺序看到文件中的4k块 - 例如,不是来自同一进程的4行... (3认同)
  • @Tilo:从这个问题来看,我理解OP希望单独的进程访问文件中一组完全不同的块(不知何故).在我看来,您提到的问题大多数对文件上的交错顺序IO有效. (2认同)
  • @Fixee:我强烈怀疑是否有可能从文件系统驱动程序外部破坏文件系统."不安全"意味着可能会出现不可预测的错误行为,就像任何竞争条件一样. (2认同)
  • 我的意思是:不能保证你总是会在同一个进程中连续看到4个块 (2认同)
  • 在打开文件为O_RDWR的Linux上,如果使用lseek/write而不是fseek/fwrite,则没有用户空间缓冲数据.只要你的写作真的不重叠,你就会得到你所期望的.例如,一个进程可以写入所有奇数字节,另一个进程可以写入所有偶数字节.这仅在您编写时未扩展文件时才有效.所有字节必须已经"存在",但稀疏分配的文件很好. (2认同)
  • @Wheezil这正是我在该项目上所做的(5年前现在),从那时起它就一直在生产中.我需要SHA1每个块到达网络时,因此需要多线程程序(否则它将受CPU限制).线程会通过lseek/write将块一次性地写入输出文件. (2认同)