O buffer será automaticamente liberado para o disco quando um processo terminar?

21

Quando redireciono a saída de um comando para um arquivo (por exemplo, echo Hello > file), esse arquivo terá garantia de ter esses dados logo após a saída do comando? Ou ainda existe uma janela muito pequena entre as saídas do comando e os dados gravados no arquivo? Gostaria de ler o arquivo logo após a saída do comando, mas não quero ler um arquivo vazio.

Eric
fonte
11
Provavelmente, ele executa o comando imediatamente, mas o tempo necessário para abrir o arquivo, gravar e fechar depende da velocidade e do tipo do seu disco rígido, de qualquer programa em execução, etc.
freginold
Em termos do exemplo dado, o que é 'o processo'? São echoe >os processos não separadas (curta duração)? E onde é que a saída de echopermanecer antes >é executada?
oɔɯǝɹ
11
@ oɔɯǝɹ >é redirecionamento de shell. É o mesmo que se o programa tivesse aberto o arquivo nomeado para gravação e substituído o stdout por ele, exatamente o que o shell faz.
Dan D.
7
Eu acho que é responsabilidade do sistema operacional fornecer o fileconteúdo, Helloindependentemente de ele ser liberado ou não.
Salman A
11
Se o programa estiver em execução na máquina A e você estiver lendo o arquivo na máquina B, com o sistema de arquivos da máquina A montado na rede, poderá acabar lendo um arquivo vazio, dependendo do tipo de sistema de rede e das configurações de montagem. Portanto, convém desativar o cache para essa montagem.
pts

Respostas:

21

Existem várias camadas de buffers / caches envolvidos.

  1. O cache da CPU.

    Os dados são reunidos byte a byte e armazenados no cache da CPU. Se o cache da CPU estiver cheio e os dados não forem acessados ​​por um tempo, o bloco que contém nossos dados pode ser gravado na memória principal. Estes são, na maioria das vezes, ocultos dos programadores de aplicativos.

  2. Os buffers em processo.

    Há alguma memória reservada no processo em que os dados são coletados, portanto, precisamos fazer o menor número possível de solicitações ao sistema operacional, porque isso é comparativamente caro. O processo copia os dados para esses buffers, que novamente podem ser suportados por caches da CPU, portanto, não há garantia de que os dados sejam copiados para a memória principal. O aplicativo precisa liberar explicitamente esses buffers, por exemplo, usando fclose (3) ou fsync (3). A função exit (3) também faz isso antes do término do processo, enquanto a função _exit (2) não , e é por isso que existe um grande aviso na página de manual para que essa função o chame apenas se você souber o que é. fazendo.

  3. Os buffers do kernel

    O sistema operacional mantém seu próprio cache, para minimizar o número de solicitações que ele precisa enviar aos discos. Esse cache não pertence a nenhum processo em particular; portanto, os dados podem pertencer a processos que já foram concluídos e, como todos os acessos passam por aqui, o próximo programa verá os dados se tiverem chegado aqui. O kernel gravará esses dados nos discos quando tiver tempo para fazê-lo ou quando solicitado explicitamente.

  4. O cache da unidade

    As unidades de disco também mantêm um cache para acelerar os acessos. Eles são gravados com bastante rapidez e existe um comando para gravar os dados restantes nos caches e relatar quando isso estiver concluído, que o sistema operacional usa no desligamento para garantir que nenhum dado seja deixado não gravado antes de desligar.

Para o seu aplicativo, é suficiente que os dados sejam registrados nos buffers do kernel (os dados reais ainda podem estar em cache da CPU neste momento e podem não ter sido gravados na memória principal): o processo "echo" termina, o que significa que quaisquer buffers em processo devem ter sido liberados e os dados entregues ao sistema operacional e, quando você inicia um novo processo, é garantido que o sistema operacional retornará os mesmos dados quando solicitado.

Simon Richter
fonte
7
Considerando o cache da CPU não parece relevante para mim. Esse é um nível desnecessário de detalhes aqui. Como analisaria todos os detalhes até que alguma quantidade física que representa um pouco em um disco rígido ou na memória ssd seja alterada para invertê-lo.
Mvw
3
De fato, o cache da CPU é bastante ortogonal.
Simon Richter
2
E o mais importante, o cache da CPU é coerente entre os núcleos, e é por isso que está totalmente fora de cena. No x86, é ainda coerente com o DMA (e o x86 possui um modo de pedido de memória com ordem total de armazenamento), portanto, qualquer coisa que possa ler memória verá os dados armazenados mais recentemente nesse endereço na ordem global das operações de memória. (Um núcleo de CPU verá suas próprias lojas antes mesmo de se tornarem visíveis globalmente, devido ao encaminhamento de lojas da fila de lojas). Em plataformas não-x86 sem DMA coerente com o cache, o kernel do Linux garante que o cache seja liberado antes do DMA para esses endereços.
Peter Cordes
11
"Estes são, na maioria das vezes, escondidos dos programadores de aplicativos." Por que o "na maior parte"? Sou desenvolvedor incorporado e, exceto durante o carregador de inicialização (não "aplicativo"), ignoro completamente o cache da CPU. Eu não acho que qualquer desenvolvedor de aplicativos possa ser afetado pelos efeitos do cache da CPU.
Sam
11
As falhas / ocorrências do cache do @Sam, juntamente com a execução especulativa, podem ser exploradas em algumas CPUs para ignorar as restrições de acesso de leitura. Talvez seja a isso que a resposta se refere?
John Dvorak
22

Se o aplicativo não tiver caches internos, as alterações serão gravadas imediatamente no arquivo. O mesmo para o seu exemplo. O arquivo é uma entidade lógica na memória que será atualizada imediatamente. Quaisquer operações subseqüentes no arquivo verão as alterações feitas pelo programa.

No entanto , isso não significa que a alteração foi gravada no disco físico. As alterações podem persistir nos caches do sistema de arquivos do SO ou no hardware. Para liberar os buffers do sistema de arquivos, use o synccomando

Gostaria de ler o arquivo logo após a saída do comando, mas não quero ler um arquivo vazio.

Você não deve encontrar problemas práticos aqui.

mtak
fonte
11
“Se o aplicativo não possui caches internos” - esse é um “if” muito grande: a grande maioria das implementações da biblioteca de E / S usa o buffer stdout por padrão. Dito isto, o padrão C, por exemplo, exige que o buffer stdout seja liberado na saída (mas potencialmente não, se não for exitpelo menos implicitamente chamado). Outras bibliotecas / linguagens (por exemplo, Java!) Oferecem menos garantias.
Konrad Rudolph
E se apenas o limitar ao primitivo de redirecionamento (ou seja, o comando na minha pergunta)? Não tem caches internos, certo?
Eric Eric
@ Eric Não, você deve ficar bem.
mtak
10
Não tenho certeza se recebo esta resposta. A questão é sobre "quando o processo termina". Todo aplicativo com caches de gravação internos os liberará no disco na saída do processo, se isso não acontecer anteriormente. IOW, esses caches não importam aqui.
precisa saber é o seguinte
2
Além disso, um buffer interno será liberado na saída ou simplesmente desaparecerá da existência, certo? Portanto, mesmo se os buffers internos não derem descarga, o conteúdo não será observável, não importa quanto tempo se espere.
WorldSEnder 26/01
21

O buffer será automaticamente liberado para o disco quando um processo terminar?

Em geral, a resposta é não .

Depende do comando. Como as outras respostas mencionam, se o comando não armazenar em buffer internamente os dados, todos os dados estarão disponíveis quando o comando terminar.

Mas a maioria, se não todas, as bibliotecas padrão de I / O que tampão stdout por padrão (até certo ponto), e dar diferentes garantias sobre a lavagem automática de buffers quando o fecha aplicação.

C garante que uma saída normal liberará os buffers . "Saída normal" significa que exité chamado - explicitamente ou retornando de main. No entanto, uma saída anormal pode contornar essa chamada (e, portanto, deixar os buffers não liberados para trás).

Aqui está um exemplo simples:

#include <signal.h>
#include <stdio.h>

int main() {
    printf("test");
    raise(SIGABRT);
}

Se você compilar e executá-lo, nãotest será necessariamente gravado no stdout.

Outras linguagens de programação dar ainda menos garantias: Java, por exemplo, faz não auto-flush após o término do programa . Se o buffer de saída contiver uma linha não terminada, ele poderá ser perdido, a menos que tenha System.out.flush()sido chamado explicitamente.

Dito isto, seu corpo pergunta pede algo ligeiramente diferente: se os dados chegam no arquivo em tudo , deve fazê-lo imediatamente após o comando termina (sujeito às advertências descritas em outras respostas).

Konrad Rudolph
fonte
7
Também vi uma saída anormal quando uma ferramenta de linha de comando está gravando em um arquivo e em stdout ou stderr, como um log de depuração, e o usuário fez um pipe para direcionar ou menos e digitou 'q' para sair menos. O arquivo do disco nem sempre é totalmente liberado se a ferramenta de linha de comando não manipular o SIGPIPE.
Zan Lynx
+1, mas "deve fazê-lo imediatamente após o comando termina" não está certo: qualquer write()ou pwrite()chamada de sistema vai acontecer antes de o processo será encerrado, e é aí que as alterações de arquivos tornam-se visíveis. Portanto, a última alteração de arquivo é definitivamente antes do término do processo, imediatamente antes, o mais tardar. Acho que, mesmo com um mmap(MAP_SHARED)arquivo, não há como observar o término do processo antes que todas as alterações no arquivo ocorram.
Peter Cordes
9

Eu acho que nenhuma pergunta aborda esse problema suficientemente ainda:

Gostaria de ler o arquivo logo após a saída do comando, mas não quero ler um arquivo vazio.

Como as outras respostas explicam, um programa com bom comportamento libera seus buffers de arquivos internos antes que o processo termine normalmente . Posteriormente, os dados ainda podem permanecer nos buffers de kernel ou hardware antes de serem gravados no armazenamento persistente. No entanto , a semântica do sistema de arquivos do Linux garante que todos os processos vejam o conteúdo dos arquivos da mesma maneira que o kernel, incluindo buffers internos 1 .

Isso geralmente é implementado tendo no máximo um buffer no kernel por objeto de arquivo e exigindo que todo o acesso ao arquivo passe por esse buffer.

  • Se um processo lê um arquivo, o kernel apresentará o conteúdo do buffer para o processo, se a parte do arquivo solicitada estiver atualmente no buffer; caso contrário, o kernel buscará os dados da mídia de armazenamento subjacente e os colocará dentro do buffer e depois voltará para a etapa anterior.

  • Se um processo gravar em um arquivo, os dados serão primeiro colocados no buffer do kernel desse arquivo. Eventualmente, o conteúdo do buffer será liberado para armazenamento. Nesse meio tempo, o acesso de leitura é satisfeito a partir do mesmo buffer (veja acima).


1 Pelo menos para arquivos regulares, diretórios e links simbólicos. FIFOs e soquetes são uma questão diferente, pois seu conteúdo nunca é armazenado de forma persistente. Existem alguns casos especiais de arquivos regulares cujo conteúdo depende de quem está perguntando; exemplos são arquivos em procfs e sysfs (pense em /proc/selfum link simbólico para o ID do processo que lê o link simbólico).

David Foerster
fonte
2
Estritamente falando, não é a semântica do sistema de arquivos do Linux que garante isso, é a semântica POSIX. Em particular, o BSD se comporta exatamente da mesma forma que o macOS e até o Windows (embora este seja um dos poucos casos em que o Windows segue a semântica do POSIX). Isso também pressupõe que ninguém está fazendo coisas estranhas com mmap()e O_DIRECT, o que pode levar a coisas fora de sincronia entre o disco e o cache da página (mas isso resolverá o momento em que o processo que está sendo executado).
Austin Hemmelgarn
2
@AustinHemmelgarn: Estritamente falando, nós dois estamos certos, já que o Linux foi projetado com o suporte para aplicativos Unix (System V) em mente e posteriormente desenvolvido para suportar o POSIX, que também baseia muitos conceitos no System V.
David Foerster
5

Supondo que seu comando seja executado por algum programa usando a biblioteca de tempo de execução C, em algum momento ele deve chamar fclosepara fechar o arquivo aberto.

A página de manual da fclosefunção C diz:

OBSERVAÇÕES Observe que fclose () libera apenas os buffers de espaço do usuário fornecidos pela biblioteca C. Para garantir que os dados sejam fisicamente armazenados no disco, os buffers do kernel também devem ser liberados, por exemplo, com sync (2) ou fsync (2).

e a página de manual para fflushtem a mesma nota. A página do manual closediz:

Um fechamento bem-sucedido não garante que os dados foram salvos com sucesso no disco, conforme o kernel adia a gravação. Não é comum um sistema de arquivos liberar os buffers quando o fluxo é fechado. Se você precisar ter certeza de que os dados estão fisicamente armazenados, use fsync (2). (Isso dependerá do hardware do disco neste momento.)

Observe que os dados estão disponíveis para outros processos, mesmo que não estejam sincronizados com a unidade. Talvez isso já seja bom o suficiente para você.

Em caso de dúvida, escreva um teste.

mvw
fonte
2
C ou não, tudo deve / deve usar o close()syscall para fechar o descritor de um arquivo.
Attie
@ Attie: Você não precisa de closearquivos antes de sair (em programas hacky que não verificam se há erros); o kernel irá limpá-los, efetivamente chamando closevocê depois que seu processo morrer. Você precisa de fclosequaisquer fluxos stdio com buffer, ou deixar a libc fazer isso por você exit(3), em oposição à chamada do sistema de saída diretamente.
Peter Cordes
Em caso de dúvida, escreva um teste. Este é um mau conselho para detectar as condições da corrida. Testar em um kernel em execução em uma peça de hardware pode dizer que a corrida não pode acontecer sob as condições de software produzidas pelo teste nesse sistema ou, se for, é muito raro detectar. Mas não é possível dizer se esse comportamento deve ser seguro em todos os sistemas de arquivos, kernels e todo o hardware (por exemplo, PowerPC). ou seja, você não pode dizer se a garantia de que depende é um detalhe da implementação ou uma garantia intencional à prova de futuro! (Neste caso é.)
Peter Cordes
Depende da situação. Algumas pessoas que tentam executar seu shell script podem ser ajudadas por esse conselho. Não era uma solução geral para ambientes mais avançados, mas menos prováveis, por exemplo, um engenheiro de software que trabalha em um kernel do sistema operacional, algumas pessoas que trabalham na atualização de microcódigo da Intel ou alguma gal que trabalha em algum sistema da ISS.
Mvw 28/0118
3

Quando redireciono a saída de um comando para um arquivo (por exemplo, echo Hello > file), esse arquivo terá garantia de ter esses dados logo após a saída do comando?

Sim. O shell abre o arquivo de saída e echosai diretamente para ele. Após o comando sair, está feito.

Ou ainda existe uma janela muito pequena entre as saídas do comando e os dados gravados no arquivo?

Se os dados já estão na mídia é outra questão, que só importa se houver uma falha de hardware posteriormente ou se você inspeciona a partição ativa com algum software forense, ignorando o sistema de arquivos montado.

Gostaria de ler o arquivo logo após a saída do comando, mas não quero ler um arquivo vazio.

Não se preocupe, o kernel mantém apenas uma visualização do arquivo, independentemente de quantas vezes ele é aberto.

Desduplicador
fonte
"o kernel mantém apenas uma visão do arquivo": não é verdade para mmap(MAP_SHARED): as lojas na região mmaped não são coerentes com as leituras do arquivo (por esse encadeamento ou outros processos). É por isso que msync(2)existe. Pelo menos é sobre isso que as páginas de manual alertam; dependendo da implementação, o Linux pode realmente mapear páginas físicas do pagecache, nesse caso, eu acho que é basicamente coerente (módulo de ordenação de memória). Enfim, tudo ainda acontece antes _exit(2).
Peter Cordes
2

Como regra geral, todos os dados pertencentes ao kernel são mantidos e limpos pelo ponto final do kernel. Esses dados incluem dados transferidos para a memória do kernel por uma chamada do sistema, como write(2).

No entanto, se o seu aplicativo (por exemplo, biblioteca C) executar um buffer em cima disso, então o kernel obviamente não tem idéia e, portanto, não garante sua limpeza.

Além disso, não acredito que exista garantia de tempo para a limpeza - ela geralmente é realizada com base no "melhor esforço" (leia-se: "quando eu tenho um segundo").

Mehrdad
fonte
Há uma garantia de que qualquer limpeza / liberação do buffer ocorrerá antes do waitpid()retorno do processo pai , se a limpeza ocorrer. ou seja, outros processos não podem observar diretamente o término do processo antes de qualquer modificação no arquivo feita por esse processo. (Eu disse "diretamente" para descartar observação indireta através de marcas de tempo de arquivos NFS, porque o cache NFS não é perfeitamente coerente entre hosts.)
Peter Cordes
@ PeterCordes: Suponho que depende do que você quer dizer com "limpeza" em vez de "manter". Para mim, "manter" é "fornecer uma visão coerente" (que tem a garantia que você mencionou) e "limpar" é "liberar para disco", o que não acredito que tenha uma garantia de tempo.
Mehrdad 28/01
Ah, entendo, você está respondendo à parte "liberada para o disco" da pergunta, que é irrelevante para o que os processos posteriores verão ao ler o arquivo. "limpar" no sentido de "limpar a memória cache / buffer suja de E / S". Certo, não há garantia de tempo, a menos que você use fsync/ fdatasync, embora o write-back do buffer no Linux inicie após /proc/sys/vm/dirty_writeback_centisecscentésimos de segundo (se não for atrasado por outro tráfego de E / S), e vários outros ajustes nesse diretório procfs também afetem as coisas (por exemplo, como grande para permitir que os buffers cresçam antes de fazer qualquer write-back).
Peter Cordes
2

Ou ainda existe uma janela muito pequena entre as saídas do comando e os dados gravados no arquivo?

Não, não existe.

Gostaria de ler o arquivo logo após a saída do comando, mas não quero ler um arquivo vazio.

Você pode ler o conteúdo final do arquivo logo após a saída do comando; nunca estará lendo o arquivo vazio. (Em C e C ++, use o espera , waitpid , wait3 ou wait4 sistema chamadas para aguardar o programa para sair, e só então ler o arquivo. Se você estiver usando uma concha, uma outra linguagem de programação ou uma biblioteca (por exemplo, a biblioteca C sistema de chamada ou a classe Java Process ), provavelmente já usa uma dessas chamadas de sistema.)

Como outras respostas e comentários apontaram, você pode acabar lendo um arquivo vazio após a saída do programa, se o programa tiver saído sem liberar seus buffers de saída internos (por exemplo, por causa de _exit , abortar ou receber um sinal fatal, ou porque é um programa Java saindo normalmente). No entanto, não há nada que você possa fazer sobre isso neste momento: os dados não liberados são perdidos para sempre, a espera adicional não os recuperará.

pts
fonte
0

sim

Desculpe por talvez adicionar outra resposta supérflua, mas a maioria parece se concentrar no arenque vermelho do título da pergunta. Mas, tanto quanto posso dizer, a questão não é sobre buffer, mas isso:

Quando redireciono a saída de um comando para um arquivo (por exemplo, eco> arquivo Hello), será garantido que esse arquivo tenha esses dados logo após a saída do comando?

Sim, incondicionalmente. O uso de ">" que você está descrevendo, junto com "|" e "<", é o modelo de processamento baseado em pipe no qual o mundo Unix e Linux se baseia fortemente. Você encontrará centenas, senão milhares de scripts, dependendo totalmente desse comportamento em todas as instalações do Linux.

Funciona como você deseja por projeto, e se houvesse a menor chance de uma condição de corrida, ela teria sido corrigida provavelmente décadas atrás.

AnoE
fonte
Isso é supérfluo, infelizmente. Apenas algumas das respostas se concentram principalmente no arenque vermelho da confirmação de dados para armazenamento não volátil. Veja a resposta da @ pts e várias outras para obter uma descrição clara: a modificação do arquivo ocorre antes da saída, ou não existe.
Peter Cordes