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).
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?