显然 POSIX 声明
文件描述符或流在它所指的打开文件描述中被称为“句柄”;一个打开的文件描述可能有多个句柄。[…] 应用程序影响第一个句柄上文件偏移量的所有活动都应暂停,直到它再次成为活动文件句柄。[...] 句柄不需要在相同的进程中应用这些规则。-- POSIX.1-2008
和
如果两个线程各自调用 [write() 函数],则每个调用要么看到另一个调用的所有指定效果,要么一个都看不到。-- POSIX.1-2008
我对此的理解是,当第一个进程发出 a
write(handle, data1, size1)
和第二个进程发出
write(handle, data2, size2)
时,写入可以以任何顺序发生,但data1
anddata2
必须是原始且连续的。
但是运行以下代码会给我带来意想不到的结果。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
die(char *s)
{
perror(s);
abort();
}
main()
{
unsigned char buffer[3];
char *filename = "/tmp/atomic-write.log";
int fd, i, j;
pid_t pid;
unlink(filename);
/* XXX Adding O_APPEND to the flags cures it. Why? */
fd = open(filename, O_CREAT|O_WRONLY/*|O_APPEND*/, 0644);
if (fd < 0)
die("open failed");
for (i = 0; i < 10; i++) {
pid = fork();
if (pid < 0)
die("fork failed");
else if (! pid) {
j = 3 + i % (sizeof(buffer) - 2);
memset(buffer, i % 26 + 'A', sizeof(buffer));
buffer[0] = '-';
buffer[j - 1] = '\n';
for (i = 0; i < 1000; i++)
if (write(fd, buffer, j) != j)
die("write failed");
exit(0);
}
}
while (wait(NULL) != -1)
/* NOOP */;
exit(0);
}
我尝试在 Linux 和 Mac OS X 10.7.4 上运行它,并使用grep -a
'^[^-]\|^..*-' /tmp/atomic-write.log
显示某些写入不连续或重叠(Linux)或完全损坏(Mac OS X)。
O_APPEND
在调用中添加标志open(2)
可解决此问题。很好,但我不明白为什么。POSIX 说
O_APPEND 如果设置,文件偏移量应设置为每次写入之前的文件末尾。
但这不是这里的问题。我的示例程序从不
lseek(2)
共享相同的文件描述,因此共享相同的文件偏移量。
我已经在 Stackoverflow 上阅读过类似的问题,但他们仍然没有完全回答我的问题。
从两个进程对文件进行原子写入并没有专门解决进程共享相同文件描述 (而不是相同文件)的情况。
如何以编程方式确定“写入”系统调用是否在特定文件上是原子的?说
POSIX 中定义的
write
调用根本没有原子性保证。
但正如上面提到的,它确实有一些。更重要的是,
O_APPEND
似乎触发了这种原子性保证,尽管在我看来,即使没有O_APPEND
.
你能进一步解释这种行为吗?