Versione breve: In quali circostanze è dd
sicuro da usare per copiare i dati, significa che non c'è rischio di danneggiamento a causa di una lettura o scrittura parziale?
Versione lunga — preambolo: dd
viene spesso utilizzato per copiare dati, in particolare da o verso un dispositivo (esempio). A volte vengono attribuite proprietà mistiche di poter accedere ai dispositivi a un livello inferiore rispetto ad altri strumenti (quando in realtà è il file del dispositivo a fare la magia), eppure dd if=/dev/sda
è la stessa cosa di cat /dev/sda
. dd
a volte si pensa che sia più veloce, ma cat
può batterlo in pratica. Tuttavia, dd
ha proprietà uniche che lo rendono davvero utile a volte.
Problema: dd if=foo of=bar
non è, infatti, lo stesso di cat <foo >bar
. Sulla maggior parte degli unice¹, dd
effettua una singola chiamata a read()
. (Trovo POSIX sfocato su ciò che costituisce "lettura di un blocco di input" in dd
.) Se read()
restituisce un risultato parziale (che, secondo POSIX e altri documenti di riferimento, è consentito a meno che la documentazione di implementazione non indichi diversamente), viene copiato un blocco parziale. Esattamente lo stesso problema esiste per write()
.
Osservazioni :In pratica, ho trovato che dd
può far fronte a dispositivi a blocchi e file regolari, ma potrebbe essere solo che non l'ho esercitato molto. Quando si tratta di pipe, non è difficile inserire dd
in colpa; ad esempio prova questo codice:
yes | dd of=out bs=1024k count=10
e controlla la dimensione del out
file (è probabile che sia ben inferiore a 10 MB).
Domanda :In quali circostanze è dd
sicuro da usare per copiare i dati? In altre parole, quali condizioni sulle dimensioni dei blocchi, sull'implementazione, sui tipi di file, ecc. possono garantire che dd
copierà tutti i dati?
(GNU dd ha un fullblock
flag per dirgli di chiamare read()
o write()
in un ciclo in modo da trasferire un blocco completo. Quindi dd iflag=fullblock
è sempre al sicuro. La mia domanda riguarda il caso in cui questi flag (che non esistono su altre implementazioni) non vengono utilizzati.)
¹
Ho controllato su OpenBSD, GNU coreutils e BusyBox.
Risposta accettata:
Dalle specifiche:
- Se il
bs=
expr
è specificato l'operando e nessuna conversione diversa dasync
,noerror
, onotrunc
sono richiesti, i dati restituiti da ciascun blocco di ingresso devono essere scritti come un blocco di uscita separato; se ilread()
restituisce meno di un blocco completo esync
conversione non è specificata, il blocco di output risultante deve avere le stesse dimensioni del blocco di input.
Quindi questo è probabilmente ciò che causa la tua confusione. Sì, perché dd
è progettato per il blocco, per impostazione predefinita parziale read()
s verrà mappato 1:1 su write()
parziale s, oppure sync
d out on tail padding NUL o caratteri spaziali su bs=
dimensione quando conv=sync
è specificato.
Ciò significa che dd
è sicuro da usare per copiare i dati (senza rischio di danneggiamento dovuto a una lettura o scrittura parziale) in ogni caso tranne uno in cui è arbitrariamente limitato da un count=
argomento, perché altrimenti dd
sarà felicemente write()
il suo output in blocchi di dimensioni identiche a quelli in cui il suo input era read()
finché non read()
s completamente attraverso di esso. E anche questo avvertimento è solo vero quando bs=
è specificato o obs=
è non specificato, come afferma la frase successiva nelle specifiche:
- Se il
bs=
expr
l'operando non è specificato o una conversione diversa dasync
,noerror
, onotrunc
viene richiesto, l'input deve essere elaborato e raccolto in blocchi di output a grandezza naturale fino al raggiungimento della fine dell'input.
Senza ibs=
e/o obs=
argomenti questo non può importare, perché ibs
e obs
sono entrambi della stessa dimensione per impostazione predefinita. Tuttavia, puoi essere esplicito sul buffer di input specificando diverse dimensioni per entrambi e non specificando bs=
(perché ha la precedenza) .
Ad esempio, se lo fai:
IN| dd ibs=1| OUT
…quindi un POSIX dd
write()
in blocchi di 512 byte raccogliendo ogni singolo read()
byte in un unico blocco di uscita.
Altrimenti, se lo fai...
IN| dd obs=1kx1k| OUT
…a POSIX dd
read()
al massimo 512 byte alla volta, ma write()
ogni blocco di output della dimensione di un megabyte (kernel che consente ed eccettuato forse l'ultimo, perché è EOF) per intero raccogliendo l'input in blocchi di output a grandezza naturale .
Anche dalle specifiche, però:
count=n
- Copia solo n blocchi di input.
count=
mappa a i?bs=
blocchi, e così via per gestire un limite arbitrario su count=
in modo portatile avrai bisogno di due dd
S. Il modo più pratico per farlo con due dd
s è convogliando l'output di uno nell'input di un altro, il che sicuramente ci pone nel regno della lettura/scrittura di un file speciale indipendentemente dal tipo di input originale.
Una pipe IPC significa che quando si specifica [io]bs=
args che, per farlo in sicurezza, devi mantenere tali valori all'interno del PIPE_BUF
definito dal sistema limite. POSIX afferma che il kernel di sistema deve garantire solo read()
atomico se write()
s entro i limiti di PIPE_BUF
come definito in limits.h
. POSIX garantisce che PIPE_BUF
essere almeno …
{_POSIX_PIPE_BUF}
- Numero massimo di byte che è garantito essere atomico durante la scrittura su una pipe.
- Valore:512
…(che è anche il dd
predefinito i/o blocksize) , ma il valore effettivo di solito è almeno 4k. Su un sistema Linux aggiornato è, per impostazione predefinita, 64k.
Quindi, quando imposti il tuo dd
processi dovresti farlo su un blocco fattore in base a tre valori:
- bs =( obs =
PIPE_BUF
o inferiore) - n =numero totale desiderato di byte letti
- conteggio =n / b
Come:
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
Devi sincronizzare i/o con dd
per gestire input non ricercabili. In altre parole, rendi espliciti i pipe-buffer e smettono di essere un problema. Ecco cosa dd
è per. L'incognita qui è yes
è la dimensione del buffer, ma se lo blocchi a un conosciuto quantità con un altro dd
quindi una piccola moltiplicazione informata può fare dd
sicura da usare per copiare i dati (senza rischio di danneggiamento a causa di una lettura o scrittura parziale) anche quando si limita arbitrariamente l'input con count=
con qualsiasi tipo di input arbitrario su qualsiasi sistema POSIX e senza perdere un singolo byte.
Ecco uno snippet delle specifiche POSIX:
ibs=
expr
- Specifica la dimensione del blocco di input, in byte, tramite
expr
(il valore predefinito è 512) .
- Specifica la dimensione del blocco di input, in byte, tramite
obs=
expr
- Specifica la dimensione del blocco di output, in byte, tramite
expr
(il valore predefinito è 512) .
- Specifica la dimensione del blocco di output, in byte, tramite
bs=
expr
- Imposta le dimensioni dei blocchi di input e output su
expr
byte, sostituendoibs=
eobs=
. Se nessuna conversione diversa dasync
,noerror
enotrunc
è specificato, ogni blocco di ingresso deve essere copiato nell'uscita come blocco singolo senza aggregare blocchi brevi.
- Imposta le dimensioni dei blocchi di input e output su
Troverai anche alcuni di questi spiegati meglio qui.