Атомность `write (2)` к локальной файловой системе

Очевидно, 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) call решает эту проблему. Приятно, но я не понимаю почему. POSIX говорит

O_APPEND Если установлено, смещение файла должно быть установлено до конца файла перед каждой записью.

но это не проблема здесь. Моя программа-пример никогда не делаетlseek(2) но с одинаковым описанием файла и таким же смещением файла.

Я уже читал похожие вопросы о Stackoverflow, но они все еще не полностью отвечают на мой вопрос.

Атомная запись в файл из двух процессов конкретно не рассматривает случай, когда процессы совместно используют один и тот жеописание файл (в отличие от того же файла).

Как программно определить, является ли системный вызов «write» атомарным для определенного файла? Говорит, чт

Thewrite @ call, как определено в POSIX, вообще нет гарантии атомарности.

Но цитируется выше у него есть. И более того,O_APPEND, кажется, вызывает эту гарантию атомарности, хотя мне кажется, что эта гарантия должна присутствовать даже безO_APPEND.

Можете ли вы объяснить это поведение?

Ответы на вопрос(4)

Ваш ответ на вопрос