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

有帮助吗?

解决方案

这“PIPE_BUF”的尺寸下的写被认为是原子的。这应该是至少512个字节,虽然它很容易被大(Linux的似乎有其设置为4096)。

这假设你在说所有完全POSIX兼容的组件。举例来说,这是不正确的NFS。

不过,假设你写信给你“O_APPEND”模式打开,并保持在“PIPE_BUF”字节的行(包括换行符)长的日志文件,你应该能够有多个作家到日志文件中没有任何腐败的问题。所有的中断会前或写入之后到达,而不是在中间。如果你希望文件以诚信求生存重新启动,你还需要调用fsync(2)后,每写,但太可怕了性能。

澄清的:阅读的意见和奥兹所罗门的回答。我不知道该O_APPEND应该有PIPE_BUF大小的原子。这是完全可能的,这是由于Linux只是如何实现write(),或者它可能是由于底层文件系统的块大小。

其他提示

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

作为提议的作者,我将为您提供一个答案,其中包含测试代码和结果的链接 升压AFIO 它实现了一个异步文件系统和文件 i/o C++ 库。

首先,O_APPEND 或 Windows 上等效的 FILE_APPEND_DATA 意味着最大文件范围(文件“长度”)的增量为 原子 在并发作家下。这是由 POSIX 保证的,Linux、FreeBSD、OS X 和 Windows 都正确实现了它。Samba 也可以正确实现它,而 v5 之前的 NFS 则不能,因为它缺乏原子附加的有线格式功能。因此,如果您以仅附加方式打开文件, 在任何主要操作系统上,并发写入都不会相互撕裂 除非涉及 NFS。

然而并发 原子追加 可能 根据操作系统、文件系统以及打开文件的标志来查看撕裂的写入 - 最大文件范围的增量是原子的,但写入相对于读取的可见性可能是原子的,也可能不是原子的。以下是按标志、操作系统和文件系统的快速总结:


无 O_DIRECT/FILE_FLAG_NO_BUFFERING:

采用 NTFS 的 Microsoft Windows 10:更新原子性 = 1 字节,直到(包括 10.0.10240),从 10.0.14393 起至少 1Mb,可能无限 (*)。

带 ext4 的 Linux 4.2.6:更新原子性 = 1 字节

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

O_DIRECT/FILE_FLAG_NO_BUFFERING:

采用 NTFS 的 Microsoft Windows 10:仅当页面对齐时,更新原子性 = 直到(包括 10.0.10240)最多 4096 字节,否则如果 FILE_FLAG_WRITE_THROUGH 关闭,则为 512 字节,否则为 64 字节。请注意,这种原子性可能是 PCIe DMA 的一个功能,而不是设计的。自 10.0.14393 起,至少 1Mb,可能无限 (*)。

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

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


您可以在以下位置查看原始实证测试结果: https://github.com/ned14/afio/tree/master/programs/fs-probe. 。请注意,我们仅在 512 字节倍数上测试撕裂偏移量,因此我无法确定 512 字节扇区的部分更新是否会在读取-修改-写入周期期间撕裂。

因此,为了回答OP的问题,O_APPEND写入不会相互干扰,但是与O_APPEND写入并发的读取可能会在带有ext4的Linux上看到撕裂的写入,除非O_DIRECT打开,因此您的O_APPEND写入需要是扇区大小的倍数。


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

以下所有功能应在Posix.1-2008中指定的效果上相对于彼此的原子量。[多种功能]...读() ...写() ...如果两个线程每个呼叫这些功能之一,则每个呼叫应查看另一个呼叫的所有指定效果,或者没有一个。 [来源]

写入可以相对于其他读取和写入进行序列化。如果可以(通过任何方式)读取文件数据后发生()在数据写()之后发生),则必须反映该write(),即使呼叫是由不同的过程进行的。 [来源]

但反过来说:

POSIX的这一卷1-2008并未指定并发的行为将来自多个进程的文件写入文件。应用程序应使用某种形式的并发控制。 [来源]

您可以在这个答案中详细了解这些含义

我写一个脚本来实证检验最大原子尺寸追加。该脚本,写在bash,生成多个工作进程,所有编写特定工人的签名相同的文件。然后读取该文件,寻找重叠或损坏的签名。您可以在此博客中看到脚本源

<强>实际的最大原子尺寸追加不仅由OS,而是由文件系统而变化。

在Linux的+ EXT3的大小是4096,而在Windows NTFS +的大小是1024。参见下面的注释用于更多的尺寸。

下面是标准说什么: http://www.opengroup.org /onlinepubs/009695399/functions/pwrite.html

  

如果文件状态标志的O_APPEND标志被设置,该文件偏移量应被设定为之前每个写入文件的端部和没有中间文件修改操作应改变文件偏移量和写入操作之间发生。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top