On-the-Fly-Stream-Komprimierung, die nicht in Hardwareressourcen übergeht?

23

Ich habe 200 GB freien Speicherplatz, 16 GB RAM (von denen ~ 1 GB durch den Desktop und den Kernel belegt ist) und 6 GB Swap.

Ich habe eine 240 GB externe SSD, von der 70 GB 1 verwendet werden und der Rest frei ist, den ich auf meiner Festplatte sichern muss.

Normalerweise würde ich zuerst dd if=/dev/sdb of=Desktop/disk.imgdie Festplatte komprimieren, aber das Erstellen des Images ist keine Option, da dies viel mehr Speicherplatz erfordern würde, als ich habe, obwohl der Komprimierungsschritt dazu führt, dass der freie Speicherplatz zusammengedrückt wird Das endgültige Archiv kann problemlos auf meine Festplatte passen.

ddSchreibt standardmäßig in STDOUT und gzipkann aus STDIN lesen. Theoretisch kann ich also schreiben dd if=/dev/sdb | gzip -9 -, aber gzipdas Lesen von Bytes dauert erheblich länger als dddas Produzieren.

Von man pipe:

An das Schreibende der Pipe geschriebene Daten werden vom Kernel gepuffert, bis sie vom Leseende der Pipe gelesen werden.

Ich stelle mir eine |wie eine echte Pipe vor - eine Anwendung schiebt Daten ein und die andere entfernt Daten so schnell wie möglich aus der Warteschlange der Pipe.

Was passiert, wenn das Programm auf der linken Seite mehr Daten schneller schreibt, als die andere Seite der Pipe darauf hoffen kann, sie zu verarbeiten? Wird es zu extremer Speicher- oder Auslagerungsauslastung führen oder wird der Kernel versuchen, ein FIFO auf der Festplatte zu erstellen, wodurch die Festplatte voll wird? Oder scheitert es einfach daran, SIGPIPE Broken pipewenn der Puffer zu groß ist?

Im Grunde läuft dies auf zwei Fragen hinaus:

  1. Was sind die Auswirkungen und Ergebnisse, wenn mehr Daten in eine Pipe verschoben werden, als gleichzeitig gelesen werden?
  2. Wie kann ein Datenstrom zuverlässig auf die Festplatte komprimiert werden, ohne dass der gesamte unkomprimierte Datenstrom auf der Festplatte gespeichert wird?

Hinweis 1: Ich kann nicht genau die ersten 70 verwendeten GB kopieren und erwarte, dass ich ein funktionierendes System oder Dateisystem erhalte, da die Fragmentierung und andere Dinge den vollständigen Inhalt erfordern.

Katze
quelle
Warum sollten Sie ein ganzes Dateisystem wie dieses sichern, anstatt nur Benutzerverzeichnisse und vielleicht eine Liste von nicht-standardmäßiger Software, die installiert ist?
Jamesqf
5
@jamesqf Eg. weil es viel einfacher zu restaurieren ist ...
deviantfan
4
@jamesqf Denn dann bekomme ich auch den Bootsektor und die Swap-Partition, damit ich die Festplatte exakt neu erstellen kann, anstatt eine Milliarde nerviger Dateien zu haben.
Katze
3
Zufälliger Tipp: Schauen Sie in lzopanstatt gzip; es komprimiert viel schneller mit nur einem geringfügig niedrigeren Kompressionsverhältnis. Ich finde es ideal für Disk-Images, bei denen die Komprimierungsgeschwindigkeit ein echter Engpass sein kann.
Marcelm
1
"Was passiert, wenn das Programm auf der linken Seite mehr Daten schneller schreibt, als die andere Seite der Pipe darauf hoffen kann, sie zu verarbeiten?" Der Kernel bewirkt, dass der Schreibvorgang unterbrochen wird, bis mehr Platz in der Pipe vorhanden ist.
Tavian Barnes

Antworten:

16

Technisch brauchen Sie nicht einmal dd:

gzip < /dev/drive > drive.img.gz

Wenn Sie tun dd, sollten Sie immer wie mit größer als Standard - Blockgröße gehen dd bs=1Moder die syscall Hölle leiden ( dd‚s Standard - Blockgröße beträgt 512 Byte, da es read()s und write()s das ist 4096syscalls pro MiB, zu viel Overhead).

gzip -9verwendet viel mehr CPU mit sehr wenig zu zeigen. Wenn gzipSie langsamer werden, verringern Sie die Komprimierungsstufe oder verwenden Sie eine andere (schnellere) Komprimierungsmethode.

Wenn Sie dateibasierte Sicherungen anstelle von ddImages ausführen , können Sie eine Logik verwenden, die entscheidet, ob die Komprimierung überhaupt durchgeführt werden soll oder nicht (dies ist für verschiedene Dateitypen nicht sinnvoll). dar( taralternative`) ist ein Beispiel, das Möglichkeiten dazu hat.

Wenn Ihr freier Speicherplatz NULL ist (da es sich um eine SSD handelt, die nach TRIM zuverlässig null zurückgibt und Sie fstrimCaches ausgeführt und gelöscht haben), können Sie ddmit conv=sparseflag auch ein unkomprimiertes, schleifenmontierbares, spärliches Image erstellen, das keinen Speicherplatz für die Nullbereiche verwendet . Erfordert, dass die Image-Datei von einem Dateisystem gesichert wird, das Dateien mit geringer Dichte unterstützt.

Alternativ gibt es für einige Dateisysteme Programme, die nur die verwendeten Bereiche abbilden können.

Frostschutz
quelle
1
"Wenn Sie dd verwenden, sollten Sie immer mit einer größeren Blockgröße als dd bs=1M" - Sie können, aber erwarten Sie nicht zu viel. Auf meinem PC ddwerden mit 512-Byte-Blöcken ca. 2GB / s gemacht. Das wird nicht der Engpass sein; gzipwird sein.
Marcelm
@ marcelm Wir wissen nie, welche Art von Maschine die Leute benutzen. Wenn Sie dd2 GB / s mit 512-Byte-Blöcken haben, wäre ich überrascht, wenn dabei nicht ein CPU-Kern zu 100% ausgeschöpft würde. Wenn es sich bei Ihrer Box um einen Quadcore handelt, der ohnehin nur im Leerlauf sitzt, werden Sie möglicherweise keinen Unterschied bemerken. Alle anderen tun es trotzdem.
Frostschutz
9
Seufzer. Jedes Mal, ddwenn die Blockgröße erwähnt wird, kommen die Leute, um zu picken. gzipCPU-intensiv zu sein war auch ein Teil meiner Antwort, okay? Und sorry, ich bin nicht einverstanden mit "vernachlässigbar". Es addiert sich vielleicht nur 1-2s pro Gig mit gzip -9(aber das sind immer noch Minuten, wenn Hunderte von Gigs bearbeitet werden), aber wenn Sie Ihren Rat befolgen, ist lzop -1es 1s pro Gig vs. 4s pro Gig. Getestet auf einer Kartoffel (Single Core Vserver). Das Hinzufügen einer vernünftigen Blockgröße ddkostet nichts und hat keine Nachteile. Nicht picken. Mach es einfach. ymmv
frostschutz
19

ddLiest und schreibt Daten blockweise und es steht immer nur ein Block aus. So

valgrind dd if=/dev/zero status=progress of=/dev/null bs=1M

zeigt, dass ddetwa 1 MB Speicher verwendet. Sie können mit der Blockgröße herumspielen und sie fallen lassen valgrind, um den Effekt auf dddie Geschwindigkeit zu sehen.

Wenn Sie in die Pipe einsteigen gzip, werden Sie ddeinfach langsamer, um gzipder Geschwindigkeit zu entsprechen. Die Speichernutzung nimmt nicht zu und der Kernel speichert die Puffer auch nicht auf der Festplatte (der Kernel weiß nicht, wie das geht, außer über Swap). Ein Rohrbruch tritt nur auf, wenn eines der Rohrenden stirbt. siehe signal(7)und write(2)für Details.

Somit

dd if=... iconv=fullblock bs=1M | gzip -9 > ...

ist eine sichere Möglichkeit, das zu tun, wonach Sie suchen.

Beim Piping wird der Schreibvorgang vom Kernel blockiert, wenn der Lesevorgang nicht mithält. Sie können dies durch Laufen sehen

strace dd if=/dev/zero bs=1M | (sleep 60; cat > /dev/null)

Sie werden sehen, dass 1 ddMB angezeigt wird, und dann ein, write()das dort sitzt und eine Minute wartet, während es ausgeführt wird sleep. So gleichen sich beide Seiten einer Pipe aus: Der Kernel blockiert das Schreiben, wenn der Schreibvorgang zu schnell ist, und blockiert das Lesen, wenn der Lesevorgang zu schnell ist.

Stephen Kitt
quelle
1
Das ist ziemlich cool. Durch welchen Mechanismus kann ddman langsamer werden, um mit gzipder Geschwindigkeit mitzuhalten? Es ist automatisch, wie vom Kernel, oder berechnet es aus Metadaten über seinen Ausgabedateideskriptor?
Katze
9
@cat Es ist automatisch; ddAufrufe write()zum Einfügen von Daten in die Pipe. write()überträgt die Kontrolle tatsächlich an den Kernel, damit dieser den Pipe-Speicher manipulieren kann. Wenn der Kernel erkennt, dass die Pipe voll ist, wartet er ("block"), bis die Pipe genug Platz hat. Erst dann wird der write()Anruf beendet und die Steuerung an zurückgegeben dd, wodurch die Daten erneut in die Pipe geschrieben werden.
Marcelm
9

Es gibt keine anderen negativen Auswirkungen als die Leistung: Die Pipe verfügt über einen Puffer, der normalerweise 64 KB groß ist. Danach wird ein Schreibvorgang in die Pipe einfach blockiert, bis gzipweitere Daten gelesen wurden.

Ulrich Schwarz
quelle
8

Beantwortung der eigentlichen Frage, wie es funktioniert: "Was passiert, wenn das Programm auf der linken Seite mehr Daten schneller schreibt, als die andere Seite der Pipe darauf hoffen kann, sie zu verarbeiten?"

Das passiert nicht. In der Pipe befindet sich ein relativ kleiner Puffer mit begrenzter Größe. siehe Wie groß ist der Pipe Buffer?

Sobald der Pipe-Puffer voll ist, blockiert das Sendeprogramm . Bei einem Schreibaufruf gibt der Kernel die Steuerung erst an das Programm zurück, wenn die Daten in den Puffer geschrieben wurden. Dies gibt dem Leseprogramm CPU-Zeit, um den Puffer zu leeren.

pjc50
quelle