どうやらPOSIXは次のように述べています
ファイル記述子またはストリームは、それが参照する開いているファイルの説明では「ハンドル」と呼ばれます。開いているファイルの説明には、複数のハンドルが含まれる場合があります。[…]最初のハンドルのファイルオフセットに影響を与えるアプリケーションによるすべてのアクティビティは、それが再びアクティブなファイルハンドルになるまで中断されます。[…]これらのルールを適用するために、ハンドルが同じプロセスにある必要はありません。--POSIX.1-2008 _
と
2つのスレッドがそれぞれ[write()関数]を呼び出す場合、各呼び出しは、他の呼び出しの指定された効果をすべて見るか、まったく見ないかのどちらかです。--POSIX.1-2008 _
これについての私の理解は、最初のプロセスがaを発行し
write(handle, data1, size1)
、2番目のプロセスが発行する
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とMacOSX 10.7.4でこれを実行してみましたgrep -a
'^[^-]\|^..*-' /tmp/atomic-write.log
が、一部の書き込みが連続していないか、重複していない(Linux)か、単純に破損している(Mac OS X)ことを示しています。
呼び出しにフラグO_APPEND
を追加すると、open(2)
この問題が修正されます。いいですが、理由がわかりません。POSIXは言う
O_APPEND設定されている場合、ファイルオフセットは、各書き込みの前にファイルの最後に設定されます。
しかし、これはここでは問題ではありません。私のサンプルプログラムは
lseek(2)
、同じファイル記述を共有するだけで、同じファイルオフセットを共有します。
Stackoverflowで同様の質問をすでに読んだことがありますが、それでも私の質問に完全には答えていません。
2つのプロセスからのファイルへのアトミック書き込みは、プロセスが(同じファイルではなく)同じファイル記述を共有する場合に特に対処していません 。
「書き込み」システムコールが特定のファイルでアトミックであるかどうかをプログラムでどのように判断しますか?と言う
POSIXで定義されている
write
呼び出しには、アトミック性の保証はまったくありません。
しかし、上で引用したように、それはいくつかあります。さらに、
このアトミック性のO_APPEND
保証は、なしでも存在するはずだと私には思えますが、このアトミック性の保証をトリガーするようですO_APPEND
。
この動作をさらに説明できますか?