Por que “ls *” demora muito mais que “ls”?

28

Eu tenho alguns arquivos em um diretório:

$ ls | wc -l
9376

Alguém pode explicar por que existe uma enorme diferença de tempo no uso de 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 de arquivos paralelos geral (GPFS). Mas também posso ver 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 *)
$
    
por Sebastian 05.05.2011 / 08:57

1 resposta

47

Quando você executa ls sem argumentos, ele simplesmente abre um diretório, lê todo o conteúdo, ordena-os e os imprime.

Quando você executa ls * , primeiro o shell expande * , que é efetivamente o mesmo que o simples ls , cria um vetor de argumento com todos os arquivos no diretório atual e chama ls . ls tem que processar 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 o primeiro (simples) ls . O processamento do shell do vetor de argumentos grandes e ls provavelmente envolverá muita alocação de memória de blocos pequenos, o que pode levar algum tempo. No entanto, como havia pouco sys e user time, mas muito real time, a maior parte do tempo teria sido gasta aguardando disco, em vez de usar CPU fazendo alocação de 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 disco lê e procura do que simplesmente ler um diretório. Eu não sei o quão caras são essas operações no seu GPFS, mas como a comparação que você mostrou para ls -l , que tem um 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 ligeiramente maior que o sistema de arquivos local em cada operação de leitura, esperaríamos que ele fosse mais pronunciado nesses casos.

A diferença entre o caso curinga e ls -l de 50% pode ser explicada pela ordenação de inodes no disco. Se os inodes foram dispostos sucessivamente na mesma ordem em que os nomes de arquivos no diretório e ls -l stat (2) editaram os arquivos na ordem de diretórios antes de classificar, ls -l possivelmente leria a maioria dos inodes em uma varredura. Com o caractere 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.

Deve-se observar que sua saída time não incluirá o tempo gasto pelo shell para expandir o curinga.

Se você realmente quiser 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.

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

    
por 05.05.2011 / 09:14