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.
linux
hard-drive
process
file-io
Eric
fonte
fonte
echo
e>
os processos não separadas (curta duração)? E onde é que a saída deecho
permanecer antes>
é executada?>
é 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.file
conteúdo,Hello
independentemente de ele ser liberado ou não.Respostas:
Existem várias camadas de buffers / caches envolvidos.
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.
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.
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.
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.
fonte
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
sync
comandoVocê não deve encontrar problemas práticos aqui.
fonte
exit
pelo menos implicitamente chamado). Outras bibliotecas / linguagens (por exemplo, Java!) Oferecem menos garantias.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 demain
. 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:
Se você compilar e executá-lo, não
test
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).
fonte
write()
oupwrite()
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 ummmap(MAP_SHARED)
arquivo, não há como observar o término do processo antes que todas as alterações no arquivo ocorram.Eu acho que nenhuma pergunta aborda esse problema suficientemente ainda:
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/self
um link simbólico para o ID do processo que lê o link simbólico).fonte
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).Supondo que seu comando seja executado por algum programa usando a biblioteca de tempo de execução C, em algum momento ele deve chamar
fclose
para fechar o arquivo aberto.A página de manual da
fclose
função C diz:e a página de manual para
fflush
tem a mesma nota. A página do manualclose
diz: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.
fonte
close()
syscall para fechar o descritor de um arquivo.close
arquivos antes de sair (em programas hacky que não verificam se há erros); o kernel irá limpá-los, efetivamente chamandoclose
você depois que seu processo morrer. Você precisa defclose
quaisquer fluxos stdio com buffer, ou deixar a libc fazer isso por vocêexit(3)
, em oposição à chamada do sistema de saída diretamente.Sim. O shell abre o arquivo de saída e
echo
sai diretamente para ele. Após o comando sair, está feito.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.
Não se preocupe, o kernel mantém apenas uma visualização do arquivo, independentemente de quantas vezes ele é aberto.
fonte
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 quemsync(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)
.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").
fonte
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.)fsync
/fdatasync
, embora o write-back do buffer no Linux inicie após/proc/sys/vm/dirty_writeback_centisecs
centé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).Não, não existe.
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á.
fonte
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:
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.
fonte