Warum scheint tar Dateiinhalte zu überspringen, wenn die Ausgabedatei / dev / null ist?


21

Ich habe ein Verzeichnis mit über 400 GiB Daten. Ich wollte prüfen, ob alle Dateien fehlerfrei gelesen werden können, so dass eine einfache Art und Weise dachte ich, war tares in /dev/null. Stattdessen sehe ich folgendes Verhalten:

$ time tar cf /dev/null .

real    0m4.387s
user    0m3.462s
sys     0m0.185s
$ time tar cf - . > /dev/null

real    0m3.130s
user    0m3.091s
sys     0m0.035s
$ time tar cf - . | cat > /dev/null
^C

real    10m32.985s
user    0m1.942s
sys     0m33.764s

Der dritte obige Befehl wurde von Ctrl+ gewaltsam gestoppt, Cnachdem er schon ziemlich lange gelaufen war. Während die ersten beiden Befehle funktionierten, war die Aktivitätsanzeige des Speichergeräts, das sie enthielt, .fast immer inaktiv. Mit dem dritten Befehl leuchtet die Anzeige konstant und bedeutet extreme Betriebsamkeit.

Wenn also festgestellt werden kann, dass tares sich um eine Ausgabedatei handelt /dev/null, dh wenn sie /dev/nulldirekt geöffnet wird, um das tarDateihandle zu haben, in das geschrieben wird, wird der Dateikörper übersprungen. (Durch Hinzufügen der vOption zum tarDrucken werden alle Dateien im Verzeichnis tar"rot" gedruckt .)

Also frage ich mich, warum das so ist? Ist es eine Art Optimierung? Wenn ja, warum sollte dann tarüberhaupt eine so zweifelhafte Optimierung für einen solchen Sonderfall durchgeführt werden?

Ich verwende GNU tar 1.26 mit glibc 2.27 unter Linux 4.14.105 amd64.


7
Als praktische Alternative sollten Sie etwas in Betracht ziehen find . -type f -exec shasum -a256 -b '{}' +. Nicht nur , dass es tatsächlich lesen und alle die Daten Prüfsumme, aber wenn Sie die Ausgabe speichern, können Sie erneut ausführen , um es später zu prüfen, ob der Inhalt der Dateien nicht verändert hat.
Ilmari Karonen

Zur Messung der Dinge , die Sie können auch verwenden pv: tar -cf - | pv >/dev/null. Das umgeht das Problem und gibt Ihnen eine Fortschrittsinformation (die verschiedenen pvOptionen)
Xenoid

Sie haben eine bekannte Fehlfunktion von GNU tar getroffen. Verwenden Sie gtar -cf /dev/zero ..., um zu bekommen, was Sie möchten.
Schily

Antworten:


25

Es ist eine dokumentierte Optimierung :

Wenn das Archiv erstellt wird /dev/null, versucht GNU tar, die Eingabe- und Ausgabeoperationen zu minimieren. Das Amanda-Backup-System hat, wenn es mit GNU tar verwendet wird, einen ersten Pass, der diese Funktion verwendet.


4
Ah, das war nicht in der Manpage beschrieben, die ich installiert hatte. Hätte es info tarstattdessen versuchen sollen ...
Ruslan

9
Sie sollten die Man & Info-Seiten wirklich synchron halten, es ist praktisch ein Fehler, den sie nicht haben
Xen2050

9
@ Ruslan Bei den meisten GNU-Dienstprogrammen enthält die Manpage nur eine kurze Zusammenfassung, die im Grunde nur dann gut genug ist, wenn Sie sich daran erinnern, dass es eine Option gibt, mit der Sie etwas tun können, ohne sich an den Namen der Option zu erinnern. Die gesamte Dokumentation hat ein Format, das sich nicht gut in Manpages übersetzen lässt und mit infooder als HTML in einem Browser verfügbar ist .
Gilles 'SO - hör auf böse zu sein'

18
Es ist ein bekanntes Problem .
Owen

8

Dies kann mit einer Vielzahl von Programmen passieren, zum Beispiel hatte ich dieses Verhalten einmal, als ich es nur benutzte cp file /dev/null. Anstatt eine Schätzung meiner Lesegeschwindigkeit zu erhalten, kehrte der Befehl nach einigen Millisekunden zurück.

Soweit ich mich erinnere, war das unter Solaris oder AIX, aber das Prinzip gilt für alle Arten von Unix-y-Systemen.

In früheren Zeiten, als ein Programm eine Datei irgendwohin kopierte, wechselte es zwischen readAufrufen, die einige Daten von der Festplatte (oder was auch immer der Dateideskriptor bezeichnet) in den Speicher abriefen (mit der Garantie, dass bei readRückkehr alles vorhanden ist ) und writeAufrufen (Die nehmen den Teil des Speichers und senden den Inhalt an das Ziel).

Es gibt jedoch mindestens zwei neuere Methoden, um dasselbe zu erreichen:

  • Linux hat Systemaufrufe copy_file_range(nicht portierbar für andere Unixe) und sendfile(etwas portierbar; ursprünglich zum Senden einer Datei an das Netzwerk gedacht, kann aber jetzt jedes Ziel verwenden). Sie sollen den Transfer optimieren. Wenn das Programm eines davon verwendet, ist es leicht vorstellbar, dass der Kernel das Ziel erkennt /dev/nullund den Systemaufruf in ein No-Op verwandelt

  • Programme können verwenden mmap, um den Dateiinhalt abzurufen read. Dies bedeutet im Wesentlichen, dass die Daten vorhanden sind, wenn ich versuche, auf diesen Speicherblock zuzugreifen, und nicht, dass die Daten vorhanden sind, wenn der Systemaufruf zurückkehrt. Ein Programm kann also mmapdie Quelldatei und dann writediesen Teil des zugeordneten Speichers aufrufen . Da beim Schreiben /dev/nulljedoch nicht auf die geschriebenen Daten zugegriffen werden muss, wird die Bedingung "Vergewissern Sie sich, dass sie vorhanden sind" nie ausgelöst, sodass die Datei auch nicht gelesen wird.

Nicht sicher , ob Gnu Teer verwendet überhaupt, und das, diese beiden Mechanismen , wenn es erkennt es schriftlich /dev/null, aber sie sind der Grund , warum jedes Programm, wenn verwendet Lesegeschwindigkeiten zu überprüfen , sollte ausgeführt werden , | cat > /dev/nullstatt > /dev/null- und warum | cat > /dev/nullsollst wird vermieden in allen anderen Fällen.


Ich denke, die Implikation auf der GNU- tarInfoseite (siehe andere Antwort) ist, dass es einen speziellen Modus dafür gibt, der vermutlich nur Dateien anzeigt, ohne sie zu öffnen. Tatsächlich habe ich nur tar cf /dev/null foo*ein paar Dateien durchgesehen und ja, nur newfstatat(..., AT_SYMLINK_NOFOLLOW)Systemaufrufe, nicht einmal eine open(), die das Atime aktualisieren könnte. Aber +1 zur Beschreibung von Mechanismen, bei denen dies passieren kann, ohne dass dies speziell erkannt werden muss.
Peter Cordes

Sollte die mmap-Erklärung "Zugriff auf die gelesenen Daten" anstelle von "Zugriff auf die geschriebenen Daten" lauten ?
Wayne Conrad

Siehe auch splice(2)unter Linux. Tatsächlich würde das Ersetzen cat > /dev/nulldurch pv -q > /dev/null(das splice()unter Linux verwendet wird) wahrscheinlich den Overhead reduzieren. Oder dd bs=65536 skip=9999999999 2> /dev/null, oder wc -c > /dev/nulloder tail -c1 > /dev/null...
Stéphane Chazelas
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.