Pourquoi «ls *» prend-il autant de temps que «ls»?

28

J'ai quelques fichiers dans un répertoire:

$ ls | wc -l
9376

Quelqu'un peut-il expliquer pourquoi il y a une si grande différence de temps dans l'utilisation de ls *et ls?

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

et

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

ok, c'est un exemple drastique et peut-être amélioré parce que le répertoire est sur un système de fichiers parallèle général (GPFS). Mais je peux également voir un ralentissement significatif sur un système de fichiers local.

MODIFIER:

$ 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

et je dois ajouter que dans mon exemple, il n'y a pas de sous-répertoires:

$ diff <(ls) <(ls *)
$
Sébastien
la source

Réponses:

47

Lorsque vous exécutez lssans arguments, il suffit d'ouvrir un répertoire, de lire tout le contenu, de les trier et de les imprimer.

Lorsque vous exécutez ls *, le shell se développe d'abord *, ce qui est en fait le même que ce que le simple a lsfait, construit un vecteur d'argument avec tous les fichiers du répertoire en cours et appelle ls. lsdoit ensuite traiter ce vecteur d'argument et pour chaque argument, et appelle access(2)¹ le fichier pour vérifier son existence. Ensuite, il imprimera la même sortie que la première (simple) ls. Le traitement par le shell du grand vecteur d'arguments et lsde celui-ci impliquera probablement beaucoup d'allocation de mémoire de petits blocs, ce qui peut prendre un certain temps. Cependant, comme il y avait peu syset le usertemps, mais beaucoup de realtemps, la plupart du temps aurait été passé à attendre pour le disque, plutôt que d' utiliser faire l' allocation de mémoire CPU.

Chaque appel à access(2)devra lire l'inode du fichier pour obtenir les informations d'autorisation. Cela signifie beaucoup plus de lectures et de recherches sur disque que la simple lecture d'un répertoire. Je ne sais pas combien ces opérations coûtent sur votre GPFS, mais comme la comparaison que vous avez montrée ls -la un temps d'exécution similaire à celui du caractère générique, le temps nécessaire pour récupérer les informations d'inode semble dominer. Si GPFS a une latence légèrement plus élevée que votre système de fichiers local à chaque opération de lecture, nous nous attendons à ce qu'elle soit plus prononcée dans ces cas.

La différence entre le cas générique et ls -lde 50% pourrait s'expliquer par la commande des inodes sur le disque. Si les inodes étaient disposés successivement dans le même ordre que les noms de fichiers dans le répertoire et ls -lstat (2) ed les fichiers dans l'ordre des répertoires avant le tri, ls -lpourrait lire la plupart des inodes dans un balayage. Avec le caractère générique, le shell triera les noms de fichiers avant de les transmettre ls, donc lslira probablement les inodes dans un ordre différent, ajoutant plus de mouvement de tête de disque.

Il convient de noter que votre timesortie n'inclura pas le temps pris par le shell pour développer le caractère générique.

Si vous voulez vraiment voir ce qui se passe, utilisez strace(1):

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

et regardez quels appels système sont effectués dans chaque cas.

¹ Je ne sais pas si elle access(2)est réellement utilisée, ou autre chose comme stat(2). Mais les deux nécessitent probablement une recherche d'inode (je ne sais pas si access(file, 0)cela contournerait une recherche d'inode).

camh
la source
2
Bonne réponse, j'étais sur le point d'en poster un similaire :) Mais oui, c'est correct, tout est question d'efficacité dans le bouclage, avec lscela il suffit de demander au système de fichiers "quels sont les enfants de l'inode pwd" où comme avec ls *il doit demander "quels sont les enfants (et quel est le fichier) de l'inode a" suivi de b, c, d, etc. etc. Une requête contre plusieurs.
NJ
@NJ one query vs many est un bon résumé jusqu'à présent. @camh: merci pour la réponse détaillée. J'ai également publié la sortie de ls -l(encore environ 30 secondes de moins ls *)
Sebastian
@Sebastian Comme camh l'a déclaré, ls -lcela prendra plus de temps que lspour stat(2)chaque fichier pour obtenir des informations sur les horodatages / informations sur le propriétaire / autorisations, etc.
NJ
6
N'oubliez pas, *globes à toutes les entrées du répertoire courant qui ne commencent pas par un point - y compris les noms des sous-répertoires. Qui sera ensuite lsédité.
Shadur
@camh: Je l' ai testé un peu plus (voir mes modifications) et a constaté que: ls< ls -l< ls -l *< ls *(j'ai toujours couru trois fois). Avec votre explication, je ne comprends pas pourquoi ls -l *est plus rapide quels *
Sebastian