从多个进程并发写入日志文件

Tom*_*ale 9 logs x11 concurrency files

在模仿 MacOS 的终端open命令或 Window 的终端命令时start此答案的注释建议将 stdout 和 stderr 附加到~/.xsession-errors,例如 ( bash):

alias open='&>>~/.xsession-errors xdg-open'
Run Code Online (Sandbox Code Playgroud)

我预见到的问题是竞争条件。lsof ~/.xsession-errors显示 22 个进程打开文件进行写入。

如何防止两个进程写入相同的偏移量~/.xsession-errors

Gil*_*il' 14

当文件以追加模式打开时,操作系统保证所有写入都发生在最后。所以来自一个作者的数据不会覆盖来自另一个作者的数据。

这仅适用于文件以追加模式打开的情况,即>>在 shell 中打开。如果文件的创建者打开它,>则该保证将不适用,并且可能具有以下序列:

  • 处理1:>out; 现在在位置 0
  • 处理2:>>out; 现在在位置 0
  • 过程 1: write hello,现在在位置 6
  • 进程 2: write world,这被写在位置 6 并且进程 2 现在在位置 12
  • 进程 1: write oops,这被写在位置 6 因为进程 1 的文件位置没有改变。

在 Debian(自 2001 年左右)上,该文件.xsession-errors由`/etc/X11/Xsession 创建,并以追加模式打开,所以一切正常:

 exec >>"$ERRFILE" 2>&1
Run Code Online (Sandbox Code Playgroud)

我不知道所有登录到~/.xsession-errors.

只要每个人都以追加模式打开文件,就会出现所有输出。然而,输出可能是碎片化的。实际上,对常规文件的足够小的写入是原子的。任何小于 512B 的东西都应该在任何地方都足够小,我认为 Linux 能保证更多¹。因此,即使有多个并发写入者,每个日志行也应该完好无损,假设写入者使用行缓冲输出并且行不太长。

¹请注意,POSIX 不保证除管道外的任何内容。


Tom*_*ale 6

>>在POSIX shell 中使用可保证文件将以O_APPEND.

Open Group 基本规范第 7 期指出

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

POSIX 定义了在一次调用中可以请求写入的最小字节数write(2)( SSIZE_MAX= 32,767 )。返回值是实际写入的字节数(保证原子性)。

然而

并非所有文件系统都符合。从多个进程附加到文件说:

需要注意的是,并非所有文件系统都兼容 POSIX。两个著名的例子是 NFS 和 Hadoop 分布式文件系统 (HDFS)。在这些网络文件系统上,追加是模拟的并且受到竞争条件的影响。

不是每个人都玩得好

虽然您可以使用 打开O_APPEND,但写入该文件的其他进程可能不会。您可以检查任何给定的文件:

lsof +fg <file>
Run Code Online (Sandbox Code Playgroud)

令人担忧的是,当我运行 时lsof +fg ~/.xsession-errors,我没有 AP看到(附加)标志,这表明我的列表中的 22 个进程(基于 Arch 的 Manjaro Linux)没有安全地打开文件。

仅当我cat >> ~/.xsession-errors在另一个 shell 中运行时,最终输出行才会包含该AP标志:

cat       3099 ravi    1w   REG   W,AP,LG   0,48      963 1926479 .xsession-errors
Run Code Online (Sandbox Code Playgroud)

如果有人知道应该在上游哪里提出这个问题,请发表评论。

几乎

如果所有进程都使用以下任一方式在本地打开文件:

  • open(2)和旗帜O_APPEND
  • fopen(3)"a"旗帜
  • POSIXsh >>bash &>>

那么任何数据都不应该通过竞争条件被覆盖。

感谢@Gilles 的回答为我指明了正确的方向。

  • 请注意,当我评论我的答案时,您误解了“SSIZE_MAX”:如果您使用“n &lt;= SSIZE_MAX”调用“write(n)”,POSIX 不保证这会写入 0 或“n”。它仅保证如果函数返回“r”,则“r”字节已被原子写入,但“r”可能小于“n”。 (2认同)