Atomicidade de `write (2)` em um sistema de arquivos local

Aparentemente POSIX afirma que

Um descritor de arquivo ou um fluxo é chamado de "identificador" na descrição do arquivo aberto a que se refere; uma descrição de arquivo aberto pode ter várias alças. [...] Toda a atividade do aplicativo que afeta o deslocamento do arquivo no primeiro identificador será suspensa até que ele se torne novamente o identificador de arquivo ativo. […] As alças não precisam estar no mesmo processo para que essas regras sejam aplicadas. -POSIX.1-2008

e

Se dois threads cada chamar [a função write ()], cada chamada deverá ver todos os efeitos especificados da outra chamada, ou nenhum deles. -POSIX.1-2008

Minha compreensão disso é que quando o primeiro processo emite umwrite(handle, data1, size1) e os segundos problemas do processowrite(handle, data2, size2), as gravações podem ocorrer em qualquer ordem, mas adata1 edata2 devo seja intocada e contígua.

Mas executar o código a seguir me dá resultados inesperados.

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

Eu tentei executar isso no Linux e Mac OS X 10.7.4 e usandogrep -a '^[^-]\|^..*-' /tmp/atomic-write.log mostra que algumas gravações não são contíguas ou se sobrepõem (Linux) ou simplesmente corrompidas (Mac OS X).

Adicionando o sinalizadorO_APPEND noopen(2) chamada corrige esse problema. Bom, mas eu não entendo porque. POSIX diz

O_APPEND Se definido, o deslocamento do arquivo deve ser definido para o final do arquivo antes de cada gravação.

mas esse não é o problema aqui. Meu programa de amostra nunca fazlseek(2) mas compartilhe a mesma descrição de arquivo e, assim, o mesmo deslocamento de arquivo.

Eu já li perguntas semelhantes no Stackoverflow, mas elas ainda não respondem totalmente à minha pergunta.

Escrita atômica em arquivo de dois processos não aborda especificamente o caso em que os processos compartilham o mesmoDescrição do arquivo (ao contrário do mesmo arquivo).

Como se determina programaticamente se a chamada do sistema “gravação” é atômica em um determinado arquivo? diz que

owrite A chamada definida no POSIX não tem garantia de atomicidade.

Mas comocitado acima tem alguns. E o que mais,O_APPEND parece desencadear esta garantia de atomicidade, embora me pareça que esta garantia deve estar presente mesmo semO_APPEND.

Você pode explicar ainda mais esse comportamento?

questionAnswers(4)

yourAnswerToTheQuestion