O acréscimo de arquivo é atômico no UNIX?

106

Em geral, o que podemos presumir quando anexamos a um arquivo no UNIX de vários processos? É possível perder dados (um processo substituindo as alterações do outro)? É possível que os dados sejam mutilados? (Por exemplo, cada processo está anexando uma linha por anexo a um arquivo de log, é possível que duas linhas sejam mutiladas?) Se o anexo não for atômico no sentido acima, qual é a melhor maneira de garantir a exclusão mútua?

Lajos Nagy
fonte

Respostas:

65

Uma gravação menor do que 'PIPE_BUF' é considerada atômica. Isso deve ter pelo menos 512 bytes, embora possa facilmente ser maior (parece que o linux está configurado para 4096).

Isso pressupõe que você está falando de todos os componentes totalmente compatíveis com POSIX. Por exemplo, isso não é verdade no NFS.

Mas supondo que você grave em um arquivo de log aberto no modo 'O_APPEND' e mantenha suas linhas (incluindo nova linha) em bytes 'PIPE_BUF', você deve ser capaz de ter vários gravadores em um arquivo de log sem problemas de corrupção. Qualquer interrupção chegará antes ou depois da gravação, não no meio. Se você deseja que a integridade do arquivo sobreviva a uma reinicialização, você também precisará ligar fsync(2)após cada gravação, mas isso é péssimo para o desempenho.

Esclarecimento : leia os comentários e a resposta de Oz Solomon . Não tenho certeza se isso O_APPENDdeveria ter esse PIPE_BUFtamanho atomicidade. É inteiramente possível que seja apenas como o Linux foi implementado write(), ou pode ser devido aos tamanhos de bloco do sistema de arquivos subjacente.

Freiheit
fonte
11
Em sistemas de arquivos lógicos, fsync(2)oferece a mesma garantia sync(2)e não tem tanto impacto sobre o desempenho.
efêmero
4
Você tem certeza sobre isso? Você poderia fornecer algum link sobre esse comportamento? Eu descobri que ele confirmou se o descritor é um pipe, mas não consegui encontrar evidências de que ele funciona para qualquer arquivo. incluindo objetos de arquivo normais não NFS.
Alan Franzoni,
6
Onde exatamente em ... / write.html? Para O_APPEND, não vejo nenhuma menção a PIPE_BUF, e vejo a promessa de que "nenhuma operação de modificação de arquivo intermediária ocorrerá entre a alteração do deslocamento do arquivo e a operação de gravação" , mas não tenho certeza se isso significa que a própria operação de gravação é ininterrupto ...
conhecido como
6
Como esta resposta indica, a declaração sobre PIPE_BUFnessa página se aplica apenas a canais e FIFOs, não a arquivos regulares.
Greg Inozemtsev
3
Com a chegada de sinais, isso pode ficar ainda pior: bugzilla.kernel.org/show_bug.cgi?id=55651 . Por que isso está marcado como uma resposta? PIPE_BUF não tem nada a ver com arquivos.
estreou em
35

Editar: Atualizado em agosto de 2017 com os resultados mais recentes do Windows.

Vou lhe dar uma resposta com links para o código de teste e os resultados como autor da proposta Boost.AFIO, que implementa um sistema de arquivos assíncrono e uma biblioteca de arquivos i / o C ++.

Em primeiro lugar, O_APPEND ou o FILE_APPEND_DATA equivalente no Windows significa que os incrementos da extensão máxima do arquivo ("comprimento" do arquivo) são atômicos sob gravadores simultâneos. Isso é garantido pelo POSIX, e Linux, FreeBSD, OS X e Windows o implementam corretamente. O Samba também o implementa corretamente, o NFS anterior à v5 não, visto que falta a capacidade de formato de fio para anexar atomicamente. Portanto, se você abrir seu arquivo apenas com acréscimo, as gravações simultâneas não serão interrompidas entre si em nenhum sistema operacional principal, a menos que o NFS esteja envolvido.

No entanto, leituras simultâneas para anexos atômicos podem ter gravações interrompidas dependendo do sistema operacional, sistema de arquivamento e quais sinalizadores você abriu o arquivo - o incremento da extensão máxima do arquivo é atômico, mas a visibilidade das gravações em relação às leituras pode ou não seja atômico. Aqui está um rápido resumo por sinalizadores, sistema operacional e sistema de arquivamento:


Não O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 com NTFS: atualizar atomicidade = 1 byte até e incluindo 10.0.10240, de 10.0.14393 pelo menos 1Mb, provavelmente infinito (*).

Linux 4.2.6 com ext4: atualizar atomicidade = 1 byte

FreeBSD 10.2 com ZFS: atualizar atomicidade = pelo menos 1Mb, provavelmente infinito (*)

O_DIRECT / FILE_FLAG_NO_BUFFERING:

Microsoft Windows 10 com NTFS: atualize atomicidade = até e incluindo 10.0.10240 até 4096 bytes apenas se a página estiver alinhada, caso contrário, 512 bytes se FILE_FLAG_WRITE_THROUGH desativado, caso contrário, 64 bytes. Observe que essa atomicidade é provavelmente um recurso do PCIe DMA ao invés de projetado. Desde 10.0.14393, pelo menos 1Mb, provavelmente infinito (*).

Linux 4.2.6 com ext4: atomicidade de atualização = pelo menos 1Mb, provavelmente infinito (*). Observe que os Linuxes anteriores com ext4 definitivamente não excediam 4096 bytes, o XFS certamente costumava ter bloqueio personalizado, mas parece que o Linux recente finalmente corrigiu isso.

FreeBSD 10.2 com ZFS: atualizar atomicidade = pelo menos 1Mb, provavelmente infinito (*)


Você pode ver os resultados do teste empírico bruto em https://github.com/ned14/afio/tree/master/programs/fs-probe . Observe que testamos os deslocamentos interrompidos apenas em múltiplos de 512 bytes, então não posso dizer se uma atualização parcial de um setor de 512 bytes seria interrompida durante o ciclo de leitura-modificação-gravação.

Portanto, para responder à pergunta do OP, as gravações O_APPEND não interferirão umas com as outras, mas as leituras simultâneas às gravações O_APPEND provavelmente verão gravações interrompidas no Linux com ext4, a menos que O_DIRECT esteja ativado, após o que as gravações O_APPEND precisariam ser um múltiplo de tamanho de setor.


(*) "Provavelmente infinito" deriva dessas cláusulas na especificação POSIX:

Todas as funções a seguir devem ser atômicas entre si nos efeitos especificados em POSIX.1-2008 quando operam em arquivos regulares ou links simbólicos ... [muitas funções] ... ler () ... escrever ( ) ... Se duas threads cada chamar uma dessas funções, cada chamada deve ver todos os efeitos especificados da outra chamada, ou nenhum deles. [Fonte]

e

As gravações podem ser serializadas em relação a outras leituras e gravações. Se uma leitura () dos dados do arquivo puder ser comprovada (por qualquer meio) para ocorrer após uma gravação () dos dados, ela deve refletir essa gravação (), mesmo se as chamadas forem feitas por processos diferentes. [Fonte]

mas inversamente:

Este volume de POSIX.1-2008 não especifica o comportamento de gravações simultâneas em um arquivo de vários processos. Os aplicativos devem usar alguma forma de controle de simultaneidade. [Fonte]

Você pode ler mais sobre o significado disso nesta resposta

Niall Douglas
fonte
29

Eu escrevi um script para testar empiricamente o tamanho máximo do anexo atômico. O script, escrito em bash, gera vários processos de trabalho que gravam assinaturas específicas de trabalho no mesmo arquivo. Em seguida, ele lê o arquivo, procurando assinaturas sobrepostas ou corrompidas. Você pode ver a fonte do script nesta postagem do blog .

O tamanho real máximo de acréscimo atômico varia não apenas pelo sistema operacional, mas também pelo sistema de arquivos.

No Linux + ext3 o tamanho é 4096, e no Windows + NTFS o tamanho é 1024. Veja os comentários abaixo para mais tamanhos.

Oz Solomon
fonte
Com qual sistema de arquivos você testou no Linux? Estou me perguntando se talvez seja baseado nos tamanhos dos blocos do sistema de arquivos.
freiheit
@freiheit, acredito que na época eu testei no ext3. Se você executá-lo em outro FS e obtiver um resultado diferente, poste um comentário.
Oz Solomon
3
@OzSolomon, usei seu script no Debian 7.8 e só consegui fazer gravações atômicas de até e incluindo 1008 bytes (1024 - 16 bytes de sobrecarga?) Em minha partição ext4 e uma montagem tmpfs. Qualquer coisa além disso resultava em corrupção todas as vezes.
Eric Pruitt
6
Seu teste parece presumir que echo $line >> $OUTPUT_FILEisso resultará em uma única chamada para, writeindependentemente do tamanho de $line.
Tomas
16

Aqui está o que o padrão diz: http://www.opengroup.org/onlinepubs/009695399/functions/pwrite.html .

Se o O_APPENDsinalizador dos sinalizadores de status do arquivo for definido, o deslocamento do arquivo deve ser definido para o final do arquivo antes de cada gravação e nenhuma operação de modificação de arquivo intermediária deve ocorrer entre a alteração do deslocamento do arquivo e a operação de gravação.

Bastien Léonard
fonte
20
“entre” - mas e as intervenções durante a escrita, que no meu entender acontecem depois do “entre”? (Ie: <change_offset_action> ... "the_between_period" ... <write_action>) - devo entender que não há garantias sobre isso?
akavel
@akavel concordou; não há garantia de que a gravação em si seja atômica. Mas estou confuso: com base na garantia fornecida em sua cotação, parece que podemos concluir que um aplicativo multithread anexando o mesmo arquivo não irá misturar partes de diferentes registros escritos. No entanto, a partir de experiências relatadas por OzSolomon, vemos que mesmo essa suposição é violada. Por quê?
máximo de
@max desculpe, eu tenho medo que eu não entendo a sua pergunta: em primeiro lugar, a experiência de OzSolomon é multi- processo , não um multi- thread (processo único) aplicativo; em segundo lugar, não entendo como você chega à conclusão de que "um aplicativo multithreaded [...] não se mistura" - isso é exatamente o que não vejo garantido pela citação de Bastien, como mencionei em meu comentário. Você pode esclarecer sua dúvida?
akavel
2
Hmm, não consigo reconstruir minha própria lógica no momento em que escrevi esse comentário ... Sim, se sua interpretação estiver correta, então é claro que os diferentes registros podem estar misturados. Mas agora que estou relendo a citação de Bastien, acho que deve significar que ninguém pode interromper "durante a escrita" - caso contrário, todo o parágrafo no padrão seria inútil, oferecendo literalmente nenhuma garantia (nem mesmo que a escrita acontecerá no final, uma vez que outra pessoa pode mover o deslocamento conforme a etapa de "gravação" está sendo executada.
máx.