Por que "ls *" demora tanto mais que "ls"?

28

Eu tenho alguns arquivos em um diretório:

$ ls | wc -l
9376

Alguém pode explicar por que existe uma diferença de horário tão grande no uso ls *e ls?

$ time ls > /dev/null
real    0m0.118s
user    0m0.106s
sys     0m0.011s

e

$ time ls * > /dev/null
real    1m32.602s
user    0m0.233s
sys     0m0.438s

ok, este é um exemplo drástico e talvez aprimorado porque o diretório está em um sistema geral de arquivos paralelos (GPFS). Mas também vejo uma desaceleração significativa em um sistema de arquivos local.

EDITAR:

$ time ls -l > /dev/null
real    0m58.772s
user    0m0.113s
sys     0m0.452s
$ time ls -l * > /dev/null
real    1m19.538s
user    0m0.252s
sys     0m0.461s

e devo acrescentar que no meu exemplo não há subdiretórios:

$ diff <(ls) <(ls *)
$
Sebastian
fonte

Respostas:

47

Quando você executa lssem argumentos, ele abre apenas um diretório, lê todo o conteúdo, classifica e imprime.

Quando você executa ls *, primeiro o shell se expande *, o que é efetivamente o mesmo que o simples ls, cria um vetor de argumento com todos os arquivos no diretório e chamadas atuais ls. lsdepois, processa esse vetor de argumento e para cada argumento e chama access(2)o arquivo para verificar sua existência. Em seguida, imprimirá a mesma saída que a primeira (simples) ls. O processamento do vetor de argumento grande e do shell lsprovavelmente envolverá muita alocação de memória de pequenos blocos, o que pode levar algum tempo. No entanto, como havia pouco syse usertempo, mas muito realtempo, a maior parte do tempo seria gasta aguardando disco, em vez de usar a CPU para alocar memória.

Cada chamada para access(2)precisará ler o inode do arquivo para obter as informações de permissão. Isso significa muito mais leituras e buscas em disco do que simplesmente ler um diretório. Não sei quão caras essas operações são no seu GPFS, mas como a comparação que você mostrou com ls -lum tempo de execução semelhante ao caso curinga, o tempo necessário para recuperar as informações do inode parece dominar. Se o GPFS tiver uma latência um pouco mais alta que o sistema de arquivos local em cada operação de leitura, esperamos que seja mais pronunciado nesses casos.

A diferença entre o caso curinga e ls -l50% pode ser explicada pela ordem dos inodes no disco. Se os inodes fossem dispostos sucessivamente na mesma ordem que os nomes dos arquivos no diretório e ls -lstat (2) editas os arquivos na ordem dos diretórios antes da classificação, ls -lpossivelmente leriam a maioria dos inodes em uma varredura. Com o curinga, o shell classificará os nomes dos arquivos antes de passá-los para ls, portanto ls, provavelmente lerá os inodes em uma ordem diferente, adicionando mais movimento da cabeça do disco.

Note-se que sua timesaída não incluirá o tempo gasto pelo shell para expandir o curinga.

Se você realmente deseja ver o que está acontecendo, use strace(1):

strace -o /tmp/ls-star.trace ls *
strace -o /tmp/ls-l-star.trace ls -l *

e veja quais chamadas do sistema estão sendo executadas em cada caso.

¹ Não sei se access(2)é realmente usado ou algo mais como stat(2). Mas ambos provavelmente exigem uma pesquisa de inode (não tenho certeza se access(file, 0)ignoraria uma pesquisa de inode.)

camh
fonte
2
Boa resposta, eu estava prestes a postar uma semelhante :) Mas sim, isso está correto, é tudo sobre eficiência no loop, com lsele pode apenas perguntar ao sistema de arquivos "para que servem os filhos do inode pwd" ls *ele deve perguntar "quais são os filhos (e qual é o arquivo) do inode a" seguido de b, c, d, etc. etc. Uma consulta versus muitas.
NJ
@NJ uma consulta vs muitos é um bom resumo até agora. @ camh: obrigado pela resposta detalhada. Eu postei a saída do ls -lbem (ainda cerca de 30 segundos a menos de ls *)
Sebastian
@Sebastian Como CAMH afirmou, ls -lvai demorar mais do que lsuma vez que tem para stat(2)cada arquivo para obter informações sobre marcas de tempo / informações do proprietário / permissões, etc.
NJ
6
Não se esqueça, *globs para todas as entradas no diretório atual que não começam com um período - incluindo os nomes dos subdiretórios. Que será então lsed.
Shadur 5/05
@camh: Eu testei um pouco mais (ver as minhas edições) e constatou que: ls< ls -l< ls -l *< ls *(eu sempre corri três vezes). Com a sua explicação, eu não entendo porque ls -l *é mais rápido quels *
Sebastian