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).