Eu tenho um diretório com mais de 400 GiB de dados nele. Eu queria verificar se todos os arquivos podem ser lidos sem erros, então uma maneira simples que pensei foi tar
em /dev/null
. Mas, em vez disso, vejo o seguinte comportamento:
$ time tar cf /dev/null .
real 0m4.387s
user 0m3.462s
sys 0m0.185s
$ time tar cf - . > /dev/null
real 0m3.130s
user 0m3.091s
sys 0m0.035s
$ time tar cf - . | cat > /dev/null
^C
real 10m32.985s
user 0m1.942s
sys 0m33.764s
O terceiro comando acima foi parado à força por Ctrl+ Cdepois de já ter sido executado por um bom tempo. Além disso, enquanto os dois primeiros comandos estavam funcionando, o indicador de atividade do dispositivo de armazenamento que continha .
estava quase sempre ocioso. Com o terceiro comando, o indicador fica constantemente aceso, o que significa extrema ocupação.
Então, parece que, quando tar
é capaz de descobrir que seu arquivo de saÃda é /dev/null
, ou seja, quando /dev/null
é aberto diretamente para ter o identificador de arquivo no qual tar
escreve, o corpo do arquivo aparece ignorado. (Adicionar v
opção para tar
imprimir todos os arquivos no diretório sendo tar
'vermelho.)
Então eu me pergunto, por que isso é assim? É algum tipo de otimização? Se sim, então por que iria tar
querer fazer uma otimização tão duvidosa para um caso tão especial?
Estou usando o GNU tar 1.26 com glibc 2.27 no Linux 4.14.105 amd64.
É uma otimização documentada :
Isso pode acontecer com uma variedade de programas, por exemplo, eu tive esse comportamento uma vez ao usar apenas
cp file /dev/null
; em vez de obter uma estimativa da velocidade de leitura do meu disco, o comando retornou após alguns milissegundos.Tanto quanto me lembro, isso foi no Solaris ou AIX, mas o princÃpio se aplica a todos os tipos de sistemas unix-y.
Antigamente, quando um programa copiava um arquivo para algum lugar, ele alternava entre
read
chamadas que pegavam alguns dados do disco (ou o que o descritor de arquivo está se referindo) para a memória (com a garantia de que tudo está lá quandoread
retorna) ewrite
chamadas (que pegam o pedaço de memória e enviam o conteúdo para o destino).No entanto, existem pelo menos duas maneiras mais recentes de alcançar o mesmo:
Linux tem chamadas de sistema
copy_file_range
(não são portáteis para outros Unixes) esendfile
(um pouco portáteis; originalmente destinado a enviar um arquivo para a rede, mas pode usar qualquer destino agora). Eles se destinam a otimizar as transferências; se o programa usa um desses, é facilmente concebÃvel que o kernel reconheça que o alvo é/dev/null
e transforme a chamada do sistema em um não-opOs programas podem usar
mmap
para obter o conteúdo do arquivo em vez deread
, isso basicamente significa "certifique-se de que os dados estejam lá quando tento acessar esse pedaço de memória" em vez de "certifique-se de que os dados estejam lá quando a chamada do sistema retornar". Assim, um programa podemmap
acessar o arquivo de origem e chamarwrite
esse pedaço de memória mapeada. No entanto, como a gravação/dev/null
não precisa acessar os dados gravados, a condição "certifique-se de que está lá" nunca é acionada, resultando na não leitura do arquivo.Não tenho certeza se o gnut tar usa algum e qual desses dois mecanismos quando detecta que está escrevendo para
/dev/null
, mas eles são a razão pela qual qualquer programa, quando usado para verificar velocidades de leitura , deve ser executado com| cat > /dev/null
em vez de> /dev/null
- e por que| cat > /dev/null
deveria ser evitado em todos os outros casos.