Warum dauert "ls *" so viel länger als "ls"?

28

Ich habe ein paar Dateien in einem Verzeichnis:

$ ls | wc -l
9376

Kann jemand erklären, warum es einen so großen Zeitunterschied bei der Verwendung von ls *und gibt ls?

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

und

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

Okay, dies ist ein drastisches Beispiel und möglicherweise erweitert, da sich das Verzeichnis in einem allgemeinen parallelen Dateisystem (GPFS) befindet. Ich kann aber auch eine deutliche Verlangsamung in einem lokalen Dateisystem feststellen.

BEARBEITEN:

$ 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

und ich sollte hinzufügen, dass es in meinem Beispiel keine Unterverzeichnisse gibt:

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

Antworten:

47

Wenn Sie lsohne Argumente arbeiten, wird lediglich ein Verzeichnis geöffnet, der gesamte Inhalt gelesen, sortiert und ausgedruckt.

Wenn Sie ausführen ls *, wird zuerst die Shell erweitert. Dies entspricht im Grunde dem *, was die einfache Shell lsgetan hat. Sie erstellt einen Argumentvektor mit allen Dateien im aktuellen Verzeichnis und ruft auf ls. lsDann muss dieser Argumentvektor und für jedes Argument verarbeitet und access(2)die Datei aufgerufen werden, um ihre Existenz zu überprüfen. Dann wird die gleiche Ausgabe wie die erste (einfache) ausgegeben ls. Sowohl die Verarbeitung des großen Argumentvektors durch die Shell als auch die Verarbeitung des großen Argumentvektors durch die Shell lswerden wahrscheinlich viel Speicherzuweisung für kleine Blöcke erfordern, was einige Zeit in Anspruch nehmen kann. Da es jedoch wenig war sysund userZeit, aber viel realZeit, hätte man die meiste Zeit verbrachte für Disk warten, anstatt CPU Speicherzuweisung zu tun.

Bei jedem Aufruf von access(2)muss der Inode der Datei gelesen werden, um die Berechtigungsinformationen abzurufen. Das bedeutet, dass viel mehr Datenträger gelesen und gesucht werden als nur ein Verzeichnis. Ich weiß nicht, wie teuer diese Vorgänge für Ihr GPFS sind, aber da der Vergleich, den Sie gezeigt haben, ls -leine ähnliche Laufzeit wie der Platzhalterfall aufweist, scheint die zum Abrufen der Inode-Informationen erforderliche Zeit zu dominieren. Wenn GPFS bei jedem Lesevorgang eine geringfügig höhere Latenz als Ihr lokales Dateisystem aufweist, ist in diesen Fällen eine stärkere Latenz zu erwarten.

Der Unterschied zwischen dem Platzhalter und ls -l50% könnte durch die Reihenfolge der Inodes auf der Festplatte erklärt werden. Wenn die Inodes nacheinander in der gleichen Reihenfolge angeordnet würden wie die Dateinamen im Verzeichnis und ls -lstat (2) die Dateien vor dem Sortieren in der Verzeichnisreihenfolge, ls -lwürden möglicherweise die meisten Inodes in einem Sweep gelesen. Mit dem Platzhalter sortiert die Shell die Dateinamen, bevor sie an sie übergeben werden ls, sodass lsdie Inodes wahrscheinlich in einer anderen Reihenfolge gelesen werden und die Bewegung des Plattenkopfs verstärkt wird.

Es ist zu beachten, dass Ihre timeAusgabe nicht die Zeit enthält, die die Shell zum Erweitern des Platzhalters benötigt.

Wenn Sie wirklich sehen möchten, was los ist, verwenden Sie strace(1):

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

und schauen Sie, welche Systemaufrufe jeweils durchgeführt werden.

¹ Ich weiß nicht, ob access(2)tatsächlich etwas verwendet wird oder etwas anderes wie stat(2). Aber beide erfordern wahrscheinlich eine Inode-Suche (Ich bin nicht sicher, ob access(file, 0)eine Inode-Suche umgangen werden würde.)

camh
quelle
2
Gute Antwort, ich wollte gerade ein ähnliches posten :) Aber ja, das ist richtig, es geht nur um Effizienz beim Looping, mit dem lsman das Dateisystem einfach fragen kann "was sind die Kinder der Inode für pwd" woher wie mit ls *es muss fragen: "Was sind die Kinder (und was ist die Datei) der Inode a", gefolgt von b, c, d usw. usw. Eine Abfrage gegen viele.
NJ
@NJ one query vs many ist bisher eine gute Zusammenfassung. @camh: danke für die ausführliche antwort. Ich habe auch die Ausgabe von ls -l(immer noch ungefähr 30 Sekunden kürzer als ls *)
Sebastian
@Sebastian Wie CAMH erwähnt, ls -lwird länger dauern , als lswie es hat stat(2)jede Datei Informationen über Zeitstempel / Eigentümerinformationen / Berechtigungen zu erhalten, usw.
NJ
6
Vergessen Sie nicht, alle Einträge im aktuellen Verzeichnis, die nicht mit einem Punkt beginnen, mit *Globs zu versehen - einschließlich der Namen der Unterverzeichnisse. Welches wird dann ed. ls
Shadur
@camh: Getestet habe ich ein bisschen mehr (meine Änderungen sehen) und festgestellt , dass: ls< ls -l< ls -l *< ls *(Ich lief es dreimal immer). Mit Ihrer Erklärung verstehe ich nicht, warum ls -l *es schneller ist alsls *
Sebastian