显然POSIX规定:
文件描述符或流都被称为指向其引用的打开文件描述符上的“句柄”;一个打开的文件描述符可以有多个句柄。[...] 应用程序对第一个句柄影响文件偏移量的所有活动都将暂停,直到它再次成为活动文件句柄。[...] 这些规则适用于不同进程中的句柄。 -- POSIX.1-2008
以及
我理解的是,当第一个进程发出如果两个线程都调用了[write()函数],则每个调用都应该看到另一个调用的所有指定效果,或者没有一个。 -- POSIX.1-2008
write(handle, data1, size1)
命令,第二个进程发出write(handle, data2, size2)
命令时,写入可以按任意顺序进行,但data1
和data2
必须同时都是原始的并且连续的。
但运行以下代码给出了意外的结果。
#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上)。
在open(2)
调用中添加O_APPEND
标志可以解决此问题。不错,但我不明白为什么。POSIX说
O_APPEND 如果设置,则在每次写入之前将文件偏移量设置为文件末尾。
但这不是问题所在。我的示例程序从不执行lseek(2)
,但共享相同的文件描述符,因此具有相同的文件偏移量。
我已经阅读了Stackoverflow上类似的问题,但它们仍然没有完全回答我的问题。
Atomic write on file from two process没有特别涉及进程共享文件描述符(而不是相同的文件)的情况。
如何通过编程确定特定文件的“写入”系统调用是否是原子性的?提到:
根据POSIX定义,
write
调用没有任何原子性保证。
但正如上面引用的内容所述,它确实具有一些原子性保证。而且更重要的是,O_APPEND
似乎触发了这种原子性保证,尽管我认为即使没有O_APPEND
,这种保证也应该存在。
你能进一步解释这种行为吗?