Kurzfassung: Unter welchen Umständen ist dd sicher zum Kopieren von Daten zu verwenden, sicher bedeutet, dass kein Risiko einer Beschädigung aufgrund eines teilweisen Lesens oder Schreibens besteht?
Langfassung – Präambel: dd wird häufig verwendet, um Daten zu kopieren, insbesondere von oder zu einem Gerät (Beispiel). Ihm wird manchmal die mystische Eigenschaft zugeschrieben, auf Geräte auf einer niedrigeren Ebene als andere Tools zugreifen zu können (wobei tatsächlich die Gerätedatei die Magie ausübt) – dennoch dd if=/dev/sda ist dasselbe wie cat /dev/sda . dd wird manchmal für schneller gehalten, aber cat kann es in der Praxis schlagen. Trotzdem dd hat einzigartige Eigenschaften, die es manchmal wirklich nützlich machen.
Problem: dd if=foo of=bar ist tatsächlich nicht dasselbe wie cat <foo >bar . Auf den meisten Unices¹, dd führt einen einzelnen Aufruf von read() durch . (Ich finde POSIX verschwommen, was das „Lesen eines Eingabeblocks“ in dd ausmacht .) Wenn read() ein Teilergebnis zurückgibt (was laut POSIX und anderen Referenzdokumenten erlaubt ist, sofern die Implementierungsdokumentation nichts anderes sagt), wird ein Teilblock kopiert. Genau das gleiche Problem besteht für write() .
Beobachtungen :In der Praxis habe ich das dd gefunden kann mit Blockgeräten und regulären Dateien umgehen, aber das kann nur daran liegen, dass ich es nicht viel trainiert habe. Wenn es um Pipes geht, ist es nicht schwierig, dd zu setzen schuld; Versuchen Sie zum Beispiel diesen Code:
yes | dd of=out bs=1024k count=10
und überprüfen Sie die Größe des out Datei (es ist wahrscheinlich weit unter 10 MB).
Frage :Unter welchen Umständen ist dd sicher zum Kopieren von Daten? Mit anderen Worten, welche Bedingungen an die Blockgrößen, an die Implementierung, an die Dateitypen usw. können dafür sorgen, dass dd werden alle Daten kopiert?
(GNU dd hat einen fullblock -Flag, um es anzuweisen, read() aufzurufen oder write() in einer Schleife, um einen ganzen Block zu übertragen. Also dd iflag=fullblock ist immer sicher. Meine Frage bezieht sich auf den Fall, wenn diese Flags (die in anderen Implementierungen nicht vorhanden sind) nicht verwendet werden.)
¹
Ich habe OpenBSD, GNU Coreutils und BusyBox überprüft.
Akzeptierte Antwort:
Aus der Spezifikation:
- Falls
bs=exprOperand ist angegeben und keine anderen Konvertierungen alssync,noerror, odernotruncangefordert werden, müssen die von jedem Eingangsblock zurückgegebenen Daten als separater Ausgangsblock geschrieben werden; wenn derread()gibt weniger als einen vollen Block und densynczurück Konvertierung nicht angegeben ist, muss der resultierende Ausgabeblock die gleiche Größe wie der Eingabeblock haben.
Das ist also wahrscheinlich der Grund für Ihre Verwirrung. Ja, weil dd ist entworfen zum Blockieren standardmäßig teilweise read() s werden 1:1 auf partielles write() abgebildet s oder sync d out on tail padding NUL oder space chars to bs= Größe, wenn conv=sync angegeben ist.
Das bedeutet, dass dd ist sicher zum Kopieren von Daten (ohne Risiko einer Beschädigung durch teilweises Lesen oder Schreiben) in jedem Fall außer einem, in dem es willkürlich durch count= begrenzt wird Argument, weil sonst dd wird gerne write() seine Ausgabe in gleichgroßen Blöcken zu denen, in denen seine Eingabe read() war bis es read() ist komplett durch. Und selbst dieser Vorbehalt ist nur wahr wenn bs= angegeben ist oder obs= ist nicht angegeben, wie der nächste Satz in der Spezifikation besagt:
- Falls
bs=exprOperand ist nicht angegeben, oder eine andere Konvertierung alssync,noerror, odernotruncangefordert wird, wird die Eingabe verarbeitet und in Ausgabeblöcken voller Größe gesammelt bis das Ende der Eingabe erreicht ist.
Ohne ibs= und/oder obs= Argumenten kann das egal sein – weil ibs und obs sind beide standardmäßig gleich groß. Sie können jedoch explizit werden über die Eingabepufferung durch Angabe verschiedener Größen für entweder und nicht Angabe von bs= (weil es Vorrang hat) .
Wenn Sie beispielsweise Folgendes tun:
IN| dd ibs=1| OUT
…dann ein POSIX dd wird write() in Blöcken von 512 Byte durch Sammeln jedes einzelne read() Byte in einen einzelnen Ausgabeblock.
Andernfalls, falls doch …
IN| dd obs=1kx1k| OUT
…ein POSIX dd wird read() maximal 512 Bytes auf einmal, aber write() jeden Megabyte großen Ausgabeblock (Kernel erlaubt und ausgenommen möglicherweise den letzten – weil das EOF ist) vollständig durch Sammeln von Eingaben in Ausgabeblöcken in voller Größe .
Aber auch aus der Spezifikation:
count=n- Nur n kopieren Eingabeblöcke.
count= wird auf i?bs= abgebildet Blöcke usw., um eine willkürliche Begrenzung von count= zu handhaben portabel benötigen Sie zwei dd s. Am praktischsten geht das mit zwei dd s besteht darin, die Ausgabe des einen in die Eingabe des anderen zu leiten, was uns sicherlich in den Bereich des Lesens/Schreibens einer speziellen Datei versetzt unabhängig vom ursprünglichen Eingabetyp.
Eine IPC-Pipe bedeutet, dass bei Angabe von [io]bs= argumentiert, dass Sie, um dies sicher zu tun, solche Werte innerhalb des vom System definierten PIPE_BUF halten müssen Grenze. POSIX gibt an, dass der Systemkern nur atomisches read() garantieren muss s und write() s innerhalb der Grenzen von PIPE_BUF wie in limits.h definiert . POSIX garantiert, dass PIPE_BUF mindestens sein …
{_POSIX_PIPE_BUF}- Maximale Anzahl von Bytes, die beim Schreiben in eine Pipe garantiert atomar sind.
- Wert:512
…(was auch der voreingestellte dd ist I/O-Blockgröße) , aber der tatsächliche Wert beträgt normalerweise mindestens 4k. Auf einem aktuellen Linux-System sind es standardmäßig 64 KB.
Wenn Sie also Ihren dd einrichten Prozesse sollten Sie es auf einem Block Faktor tun basierend auf drei Werten:
- bs =( obs =
PIPE_BUFoder weniger ) - n =gewünschte Gesamtzahl gelesener Bytes
- count =n / bs
Wie:
yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s
Sie müssen i/o mit dd synchronisieren um nicht durchsuchbare Eingaben zu behandeln. Mit anderen Worten, machen Sie Pipe-Puffer explizit und sie stellen kein Problem mehr dar. Das ist dd ist für. Die unbekannte Größe ist hier yes Puffergröße – aber wenn Sie das für einen bekannten ausschließen Menge mit einem anderen dd dann kann eine wenig informierte Multiplikation dd ergeben sicher zum Kopieren von Daten (ohne Risiko der Beschädigung durch teilweises Lesen oder Schreiben) selbst wenn die Eingabe willkürlich mit count= eingeschränkt wird mit jedem beliebigen Eingabetyp auf jedem POSIX-System und ohne ein einziges Byte zu verpassen.
Hier ist ein Ausschnitt aus der POSIX-Spezifikation:
ibs=expr- Geben Sie die Eingangsblockgröße in Bytes mit
expran (Standard ist 512) .
- Geben Sie die Eingangsblockgröße in Bytes mit
obs=expr- Geben Sie die Ausgabeblockgröße in Bytes mit
expran (Standard ist 512) .
- Geben Sie die Ausgabeblockgröße in Bytes mit
bs=expr- Stellen Sie sowohl die Eingangs- als auch die Ausgangsblockgröße auf
exprein Bytes, ersetztibs=undobs=. Wenn keine Konvertierung außersync,noerror, undnotruncangegeben ist, wird jeder Eingabeblock als einzelner Block in die Ausgabe kopiert, ohne kurze Blöcke zu aggregieren.
- Stellen Sie sowohl die Eingangs- als auch die Ausgangsblockgröße auf
Einiges davon wird hier auch besser erklärt.