UNIX中的文件附加是原子的吗?

UNIX中的文件附加是原子的吗?,unix,file-io,posix,atomic,atomicity,Unix,File Io,Posix,Atomic,Atomicity,通常,当我们从多个进程附加到UNIX中的文件时,什么是理所当然的?是否可能丢失数据(一个进程覆盖另一个进程的更改)?数据是否可能被破坏?(例如,每个进程每次向日志文件追加一行,是否可能有两行被破坏?)如果追加在上述意义上不是原子的,那么确保互斥的最佳方式是什么?小于“PIPE_BUF”大小的写入应该是原子的。这应该至少是512字节,尽管它可能会更大(linux似乎将其设置为4096) 这假设您正在谈论所有完全符合POSIX的组件。例如,在NFS上不是这样 但是,假设您写入以“O_APPEND”模

通常,当我们从多个进程附加到UNIX中的文件时,什么是理所当然的?是否可能丢失数据(一个进程覆盖另一个进程的更改)?数据是否可能被破坏?(例如,每个进程每次向日志文件追加一行,是否可能有两行被破坏?)如果追加在上述意义上不是原子的,那么确保互斥的最佳方式是什么?

小于“PIPE_BUF”大小的写入应该是原子的。这应该至少是512字节,尽管它可能会更大(linux似乎将其设置为4096)

这假设您正在谈论所有完全符合POSIX的组件。例如,在NFS上不是这样

但是,假设您写入以“O_APPEND”模式打开的日志文件,并将行(包括换行符)保持在“PIPE_BUF”字节之下,那么您应该能够在没有任何损坏问题的情况下将多个写入器写入日志文件。任何中断都会在写入之前或之后到达,而不是在中间。如果您想让文件完整性在重新启动后继续存在,您还需要在每次写入后调用
fsync(2)
,但这对性能很糟糕


澄清:阅读评论和评论。我不确定
O\u APPEND
是否应该具有
PIPE\u BUF
大小原子性。这完全可能是Linux实现的
write()
,也可能是由于底层文件系统的块大小。

标准是这样说的:

如果设置了文件状态标志的
O_APPEND
标志,则应在每次写入之前将文件偏移量设置为文件的末尾,并且在更改文件偏移量和写入操作之间不应发生干涉文件修改操作


我写了一个脚本来测试最大的原子大小。用bash编写的脚本生成多个工作进程,这些进程都将特定于工作进程的签名写入同一个文件。然后读取文件,查找重叠或损坏的签名。您可以在此处查看脚本的源代码

实际最大原子追加大小不仅因操作系统而异,还因文件系统而异。


在Linux+ext3上,大小为4096,在Windows+NTFS上,大小为1024。有关更多尺寸,请参见下面的注释。

编辑:更新了2017年8月的最新Windows结果

我将给您一个答案,它是测试代码和结果的链接,它是作者提出的异步文件系统和文件I/O C++库的作者。 首先,Windows上的O_APPEND或等效文件_APPEND_数据意味着在并发写入程序下,最大文件范围(文件“长度”)的增量是原子的。POSIX保证了这一点,Linux、FreeBSD、OSX和Windows都正确地实现了这一点。Samba也正确地实现了它,v5之前的NFS则没有,因为它缺乏以原子方式追加的有线格式功能。因此,如果您仅使用append打开文件,则除非涉及NFS,否则在任何主要操作系统上,并发写操作都不会彼此撕裂

但是,根据操作系统、归档系统以及打开文件时使用的标志,对原子附件的并发读取可能会看到写操作被破坏-最大文件范围的增量是原子的,但相对于读取的写入操作的可见性可能是原子的,也可能不是原子的。以下是根据标志、操作系统和归档系统进行的快速总结:


无O_直接/文件标志\u无缓冲: 使用NTFS的Microsoft Windows 10:update atomicity=1字节,直到并包括10.0.10240,从10.0.14393至少1Mb,可能是无限(*)

Linux 4.2.6和ext4:update atomicity=1字节

带ZFS的FreeBSD 10.2:更新原子性=至少1Mb,可能无限(*)

O\u直接/文件\u标志\u无\u缓冲: Microsoft Windows 10 with NTFS:update atomicity=until并包括10.0.10240,仅当页面对齐时,最多4096字节;否则,如果文件\u标志\u写入\u至关闭,则为512字节,否则为64字节。请注意,此原子性可能是PCIe DMA的一项功能,而不是在中设计的。从10.0.14393开始,至少1Mb,可能是无限(*)

Linux 4.2.6和ext4:update atomicity=至少1Mb,可能是无限(*)。请注意,早期带有ext4的Linuxes肯定没有超过4096字节,XFS当然曾经有自定义锁定,但看起来最近的Linux终于解决了这个问题

带ZFS的FreeBSD 10.2:更新原子性=至少1Mb,可能无限(*)


您可以在上看到原始的实证测试结果。注意,我们只在512字节的倍数上测试被撕裂的偏移量,所以我不能说512字节扇区的部分更新是否会在读-修改-写周期中被撕裂

因此,为了回答OP的问题,O_追加写入不会相互干扰,但在使用ext4的Linux上,与O_追加写入并发的读取可能会看到写操作被撕裂,除非O_DIRECT处于启用状态,因此O_追加写入需要是扇区大小的倍数


(*)“可能无限”源于POSIX规范中的以下条款:

以下所有功能均应为原子功能 在POSIX.1-2008中规定的作用下,在 常规文件或符号链接。。。[许多功能]。。。read()。。。 write()。。。如果两个线程分别调用其中一个函数,则每次调用 应看到其他调用的所有指定效果,或 没有

写入可以相对于其他读取和写入进行序列化。如果 文件数据的read()可以(通过任何方式)在 数据的write(),它必须反映write(),即使调用 由不同的工艺制成

但反过来说:

POSIX.1-2008的这一卷没有规定并发的行为 从多个进程写入文件。应用程序应该使用一些 并发控制的形式

在健全的文件系统上