По-видимому, 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).
Добавление флага O_APPEND
в вызов open(2)
устраняет это
проблема. Приятно, но я не понимаю, почему. POSIX говорит
O_APPEND Если установлено, смещение файла должно быть установлено до конца файла перед каждой записью.
но это не проблема. Моя примерная программа никогда не делает
lseek(2)
, но использовать одно и то же описание файла и, таким образом, тот же файл
смещение.
Я уже читал подобные вопросы в Stackoverflow, но они все еще не полностью отвечайте на мой вопрос.
Атомная запись в файл из двух процессов обратитесь к случаю, когда процессы используют одно и то же описание файла (в отличие от того же файла).
Как программно определить, является ли системный вызов "write" атомарным в конкретном файле? говорит, что
Вызов
write
, определенный в POSIX, вообще не имеет гарантии на атомарность.
Но как приведенное выше, оно имеет некоторые. И что еще,
O_APPEND
, похоже, запускает эту гарантию атомарности, хотя кажется
для меня эта гарантия должна присутствовать даже без O_APPEND
.
Можете ли вы подробнее объяснить это поведение?