Quando è adatto per copiare i dati? (o, quando sono read () e write () parziale)


60

Versione breve: in quali circostanze è ddsicuro utilizzare per la copia dei dati, il che significa che non vi è alcun rischio di corruzione a causa di una lettura o scrittura parziale?

Versione lunga - preambolo: dd è spesso usato per copiare dati, specialmente da o verso un dispositivo ( esempio ). A volte sono attribuite proprietà mistiche di poter accedere ai dispositivi a un livello inferiore rispetto ad altri strumenti (quando in realtà è il file del dispositivo che sta facendo la magia) - eppure dd if=/dev/sdaè la stessa cosa di cat /dev/sda. dda volte è pensato per essere più veloce, ma catin pratica può batterlo . Tuttavia, ddha proprietà uniche che a volte lo rendono davvero utile .

Problema: dd if=foo of=bar in realtà non è lo stesso di cat <foo >bar. Sulla maggior parte unices¹, ddfa una singola chiamata a read(). (Trovo POSIX sfocato su ciò che costituisce la "lettura di un blocco di input" dd). Se read()restituisce un risultato parziale (che, secondo POSIX e altri documenti di riferimento, è consentito a meno che la documentazione di implementazione non dica diversamente), viene copiato un blocco parziale. Esattamente lo stesso problema esiste per write().

Osservazioni : in pratica, ho scoperto che ddpuò far fronte a dispositivi a blocchi e file regolari, ma potrebbe essere solo che non mi sono esercitato molto. Quando si tratta di tubi, non è difficile ddrimproverare; ad esempio prova questo codice :

yes | dd of=out bs=1024k count=10

e controlla la dimensione del outfile (è probabile che sia inferiore a 10 MB).

Domanda : in quali circostanze è ddsicuro utilizzare per la copia dei dati? In altre parole, quali condizioni sulle dimensioni del blocco, sull'implementazione, sui tipi di file, ecc., Possono garantire che ddcopierà tutti i dati?

( GNU dd ha un fullblockflag per dirgli di chiamare read()o write()in un ciclo in modo da trasferire un blocco completo. Quindi dd iflag=fullblockè sempre sicuro. La mia domanda riguarda il caso in cui questi flag (che non esistono su altre implementazioni) non vengono utilizzati .)

¹ Ho verificato OpenBSD, GNU coreutils e BusyBox.


Non ho mai visto alcun sistema Unixy in grado di leggere alcuni MiB in una sola lettura (2) ...
vonbrand

3
Durante l'utilizzo count, iflag=fullblockè obbligatorio (o, in alternativa, iflag=count_bytes). Non c'è oflag=fullblock.
frostschutz,

Risposte:


10

Dalle specifiche :

  • Se bs=exprviene specificato l' operando e nessuna conversione diversa da sync, noerroro notruncrichiesta, i dati restituiti da ciascun blocco di input devono essere scritti come blocchi di output separati; se il valore read()restituito è inferiore a un blocco completo e la syncconversione 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ì, poiché ddè progettato per il blocco, per impostazione predefinita i read()s parziali verranno mappati da 1: 1 a write()s parziali , oppure verranno rimpiazzati syncsul pad di coda NUL o i caratteri spaziali alla bs=dimensione quando conv=syncviene specificato.

Ciò significa che ddè sicuro da usare per la copia dei dati (senza rischio di corruzione a causa di una lettura o scrittura parziale) in ogni caso tranne uno in cui è arbitrariamente limitato da un count=argomento, perché altrimenti ddsarà felicemente il write()suo output in blocchi di dimensioni identiche a quelli in cui il suo input era read()fino a quando non read()è completamente attraverso di esso. E anche questo avvertimento è vero solo quando bs=è specificato o nonobs= è specificato, come afferma la frase successiva nelle specifiche:

  • Se l' bs=exproperando non è specificato o una conversione diversa da sync, noerroro notruncè richiesta, l'input deve essere elaborato e raccolto in blocchi di output full-size fino al raggiungimento della fine dell'ingresso.

Senza ibs=e / o obs=argomenti questo non può importare, perché ibse obshanno entrambi la stessa dimensione di default. Tuttavia, è possibile ottenere espliciti sul buffer di input specificando dimensioni diverse per entrambi e non specificando bs= (poiché ha la precedenza) .

Ad esempio, se lo fai:

IN| dd ibs=1| OUT

... quindi un POSIX ddverrà write()suddiviso in blocchi di 512 byte raccogliendo ogni singolo read()byte in un singolo blocco di output.

Altrimenti, se lo fai ...

IN| dd obs=1kx1k| OUT

... un POSIX ddavrà un read() massimo di 512 byte alla volta, ma write()ogni blocco di output di dimensioni megabyte (kernel che consente ed esclude possibilmente l'ultimo - perché quello è EOF) per intero raccogliendo l'input in blocchi di output full-size .

Anche dalle specifiche, però:

  • count=n
    • Copia solo n blocchi di input.

count=esegue la mappatura su i?bs=blocchi, quindi per gestire un limite arbitrario su count=portabile avrai bisogno di due ddsecondi. Il modo più pratico per farlo con due dds è convogliare l'output di uno nell'input di un altro, il che sicuramente ci mette 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=sostiene che, per farlo in modo sicuro, è necessario mantenere tali valori entro il PIPE_BUFlimite definito dal sistema . POSIX afferma che il kernel del sistema deve garantire solo atomiche read()s e write()s nei limiti delle PIPE_BUFquali definite limits.h. POSIX garantisce che PIPE_BUFavere un almeno ...

  • {_POSIX_PIPE_BUF}
    • Numero massimo di byte che è garantito essere atomico quando si scrive su una pipe.
    • Valore: 512

... (che è anche il ddblocco predefinito di I / O predefinito ) , ma il valore effettivo è di solito almeno 4k. Su un sistema Linux aggiornato è, per impostazione predefinita, 64k.

Quindi, quando configuri i tuoi ddprocessi, dovresti farlo su un fattore di blocco basato su tre valori:

  1. bs = (obs = PIPE_BUFo inferiore)
  2. n = numero totale desiderato di byte letti
  3. conteggio = n / bs

Piace:

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 / ow / ddper gestire input non ricercabili. In altre parole, rendere espliciti i buffer di pipe e cessano di essere un problema. Questo è ciò che ddserve. La quantità sconosciuta qui è yesla dimensione del buffer - ma se la blocchi a una quantità nota con un'altra, dduna moltiplicazione informata può rendere dd sicuro l'uso per la copia dei dati (senza rischio di corruzione a causa di una lettura o scrittura parziale) anche quando si limita arbitrariamente l'ingresso w / count=w / qualsiasi tipo di ingresso arbitrario su qualsiasi sistema POSIX e senza perdere un singolo byte.

Ecco uno snippet dalle specifiche POSIX :

  • ibs=expr
    • Specificare la dimensione del blocco di input, in byte, per (il valore predefinito è 512) .expr
  • obs=expr
    • Specificare la dimensione del blocco di output, in byte, per (il valore predefinito è 512) .expr
  • bs=expr
    • Impostare le dimensioni dei blocchi di input e output su exprbyte, sostituendo ibs=e obs=. Se nessuna conversione diversa da sync, noerrored notruncè specificata, ciascun blocco di ingresso deve essere copiato nell'uscita come blocco singolo senza aggregare blocchi corti.

Troverai anche alcuni di questi spiegati meglio qui .


5

Con socket, pipe o ttys, read () e write () possono trasferire meno della dimensione richiesta, quindi quando si utilizza dd su questi, è necessario il flag fullblock. Con file regolari e dispositivi a blocchi, tuttavia, ci sono solo due volte in cui possono fare una breve lettura / scrittura: quando si raggiunge EOF o se si verifica un errore. Questo è il motivo per cui le implementazioni precedenti di dd senza il flag fullblock erano sicure da usare per la duplicazione del disco.


È vero per tutti i moderni unici? (So ​​che a un certo punto non era vero per Linux, forse fino a 2.0.xo 2.2.x. Ricordo che fallivo mke2fssilenziosamente perché si chiamava write()con una dimensione non power-of-2 (3kB IIRC) e il kernel arrotondato fino a una potenza di 2.)
Gilles 'SO- smetti di essere malvagio' il

@Gilles che suona come un problema completamente diverso. Devi sempre usare un multiplo della dimensione del blocco corretta con i dispositivi a blocchi. Sono abbastanza sicuro che sia vero per tutti gli Unicami, ed è vero anche per Windows.
psusi,

A parte i nastri, la dimensione del blocco di un dispositivo è puramente preoccupata per il kernel. cat </dev/sda >/dev/sdbfunziona bene per clonare un disco.
Gilles 'SO- smetti di essere malvagio' il

@Gilles perché il gatto usa la dimensione del blocco appropriata, come notato da OrbWeaver nella sua risposta.
psusi,

No, non esiste una "dimensione del blocco appropriata". catseleziona una dimensione del buffer per le prestazioni; non ottiene alcuna informazione relativa al dispositivo dal kernel. Oltre ai nastri, puoi read()e write()su un dispositivo a blocchi di qualsiasi dimensione. Almeno su Linux, st_blksizedipende solo dal filesystem in cui si trova l'inode del dispositivo a blocchi, non dal dispositivo sottostante.
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.