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

По-видимому, POSIX утверждает, что

Либо файловый дескриптор, либо поток называется "дескриптором" на открыть описание файла, на который он ссылается; описание открытого файла может иметь несколько ручек. […] Вся активность приложения влияющие на смещение файла на первом дескрипторе должны быть приостановлены пока он снова не станет активным дескриптором файла. […] Ручки нужны не находиться в одном и том же процессе для применения этих правил. -- POSIX.1-2008

и

Если каждый из двух потоков вызывает [функцию write()], каждый вызов должен либо увидеть все указанные эффекты другого вызова, либо ничего их. -- POSIX.1-2008

Насколько я понимаю, когда первый процесс выдает write(handle, data1, size1)и второй процесс выдает write(handle, data2, size2)записи могут выполняться в любом порядке, кроме данные1и данные2должныбыть как чистыми, так и непрерывными.

Но запуск следующего кода дает неожиданные результаты.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
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, определенный в POSIX, вообще не гарантирует атомарности.

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

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

17
задан Community 23 May 2017 в 10:30
поделиться