Atomowość `write (2)` do lokalnego systemu plików

Najwyraźniej POSIX to potwierdza

Deskryptor pliku lub strumień nazywany jest „uchwytem” w opisie otwartego pliku, do którego się odnosi; otwarty opis pliku może mieć kilka uchwytów. […] Cała aktywność aplikacji mająca wpływ na przesunięcie pliku na pierwszym uchwycie zostanie zawieszona, aż ponownie stanie się aktywnym uchwytem pliku. […] Uchwyty nie muszą być w tym samym procesie, aby można było zastosować te zasady. -POSIX.1-2008

i

Jeśli dwa wątki wywołują każde wywołanie [funkcja write ()], każde wywołanie będzie albo widzieć wszystkie określone efekty drugiego wywołania, albo żadne z nich. -POSIX.1-2008

Moje rozumienie tego jest takie, gdy pierwszy proces wydajewrite(handle, data1, size1) i drugi problem z procesemwrite(handle, data2, size2), zapisy mogą wystąpić w dowolnej kolejności, aledata1 idata2 musi bądź nieskazitelny i ciągły.

Ale uruchomienie następującego kodu daje mi nieoczekiwane wyniki.

#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);
}

Próbowałem uruchomić to na Linuksie i Mac OS X 10.7.4 i użyćgrep -a '^[^-]\|^..*-' /tmp/atomic-write.log pokazuje, że niektóre zapisy nie są ciągłe lub nakładają się (Linux) lub zwykłe uszkodzone (Mac OS X).

Dodawanie flagiO_APPEND wopen(2) wywołanie naprawia ten problem. Fajnie, ale nie rozumiem dlaczego. POSIX mówi

O_APPEND Jeśli ustawione, przesunięcie pliku należy ustawić na końcu pliku przed każdym zapisaniem.

ale to nie jest problem tutaj. Mój przykładowy program nigdy nie działalseek(2) ale dziel się tym samym opisem pliku, a tym samym tym samym przesunięciem pliku.

Przeczytałem już podobne pytania na temat Stackoverflow, ale wciąż nie w pełni odpowiadają na moje pytanie.

Zapis atomowy w pliku z dwóch procesów nie odnosi się konkretnie do przypadku, w którym procesy są takie sameOpis pliku (w przeciwieństwie do tego samego pliku).

Jak programowo określić, czy wywołanie systemowe „write” jest atomowe dla konkretnego pliku? mówi że

Thewrite Wywołanie zdefiniowane w POSIX nie ma żadnej gwarancji atomowości.

Ale jakocytowany powyżej to ma trochę. I co więcej,O_APPEND wydaje się wywoływać tę gwarancję atomowości, chociaż wydaje mi się, że ta gwarancja powinna być obecna nawet bezO_APPEND.

Czy możesz wyjaśnić dalej to zachowanie?

questionAnswers(4)

yourAnswerToTheQuestion