Come si può usare `dd` per spostare i blocchi di dati a destra?


10

Considera un dispositivo a blocchi non elaborati da 100 MB come semplice esempio. Sono 204800 blocchi di 512 byte ciascuno per un totale di 102760448 byte.

La sfida è quella di spostare i primi 98 MB (200704 blocchi) in modo che vi sia uno spazio vuoto di 2 MB (4096 blocchi) davanti. Per fare questo sul posto è necessario che nulla sia scritto in un settore che non è stato letto. Un modo per raggiungere questo obiettivo è introdurre un buffer:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 | dd of=/dev/sdj2 seek=4096

L'aspettativa è che mbuffermemorizzerà 4096 blocchi prima di passare qualsiasi cosa allo scrittore, assicurando così che nulla sia scritto in un'area che non è stata letta e che lo scrittore è in ritardo del lettore per le dimensioni del buffer. Il buffer dovrebbe consentire al lettore e allo scrittore di operare il più velocemente possibile all'interno di tali costrutti.

Tuttavia, non sembra funzionare in modo affidabile. Ho provato a utilizzare dispositivi reali ma non funziona mai su di essi, mentre gli esperimenti con un file hanno funzionato sulla mia casella a 64 bit ma non sulla mia casella a 32 bit.

Innanzitutto, alcuni preparativi:

$ dd if=/dev/sdj2 count=200704 | md5sum
0f0727f6644dac7a6ec60ea98ffc6da9
$ dd if=/dev/sdj2 count=200704 of=testfile

Questo non funziona:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=/dev/sdj2 seek=4096
summary: 98.0 MiByte in  4.4sec - average of 22.0 MiB/s
md5 hash: 3cbf1ca59a250d19573285458e320ade

Funziona su un sistema a 64 bit ma non su un sistema a 32 bit:

$ dd if=testfile count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=testfile seek=4096 conv=notrunc
summary: 98.0 MiByte in  0.9sec - average of  111 MiB/s
md5 hash: 0f0727f6644dac7a6ec60ea98ffc6da9

Come può essere fatto in modo affidabile?


Appunti

Ho letto altre domande sul buffering e ho guardato pv, buffere mbuffer. Potevo solo far funzionare quest'ultimo con la dimensione del buffer richiesta.

L'uso della memoria intermedia è una soluzione ovvia al problema che funziona sempre, ma non è pratico quando non è disponibile una capacità di riserva sufficiente.

Piattaforme di prova che eseguono Arch Linux con la mbufferversione 20140302.


Suppongo che non risolverebbe il problema, ma per curiosità perché usarlo mbuffer? Perché non invece fare ddleggere l'intero contenuto del dispositivo a blocchi in una volta sola usando dd bs=102760448? Certo, in un modo o nell'altro è bufferizzato nella RAM.
Celada,

@Celada: l'esempio da 100 MB era solo un esempio. Leggere 1 TB, ad esempio, in una volta sola non sarebbe una buona idea.
Starfry,

2
Ah, adesso capisco, grazie. La mbufferrealtà dovrebbe forzare il secondo ddad essere in ritardo per il primo e avete solo bisogno di abbastanza RAM per tamponare la dimensione dello spostamento. Peccato ddche non supporti la lettura e la scrittura di blocchi in ordine inverso poiché ciò eliminerebbe il problema!
Celada,

Non hai elencato come hai calcolato il secondo md5sum
psusi il

@psusi, il secondo md5 è prodotto da mbuffer (il suo -Hargomento abilita questa funzione).
Starfry,

Risposte:


2

Senza un buffer, potresti andare indietro, un blocco alla volta.

for i in $(seq 100 -1 0)
do
    dd if=/dev/thing of=/dev/thing \
       bs=1M skip=$i seek=$(($i+2)) count=1
done

Si noti che questo esempio è pericoloso a causa della mancanza di controllo degli errori.

È anche lento a causa della quantità di ddchiamate. Se hai memoria da risparmiare, puoi usare un blocco di dimensioni maggiori.

Con un buffer, fai attenzione alle insidie . E ' non è sufficiente a garantire un pre-riempimento 100%. Ciò di cui hai bisogno è un riempimento minimo durante l'intero processo. Il buffer non deve mai scendere sotto 2Mperché altrimenti avrai sovrascritto nuovamente i tuoi dati ancora da leggere.

Quindi, mentre in teoria potresti fare a meno di qualsiasi tipo di buffer e solo catena dd:

dd if=/dev/thing bs=1M | \
dd bs=1M iflag=fullblock | \
dd bs=1M iflag=fullblock | \
dd of=/dev/thing bs=1M seek=2

In pratica questo non funziona in modo affidabile perché non vi è alcuna garanzia che il primo ddriesca a continuare a leggere i dati, mentre l'ultimo dd(con 2M"buffer" nel mezzo) sta già scrivendo.

È possibile aumentare considerevolmente le possibilità aumentando notevolmente il buffer tra, ma anche così, non è affidabile.

Purtroppo non conosco un buon programma di buffer con proprietà di riempimento minimo. È necessario uno che arresta l'output fintanto che nel buffer è presente un margine di sicurezza inferiore al margine di sicurezza.


Ho accettato questo perché risponde alla domanda originale dimostrando come ddpotrebbe essere usato. Penso, tuttavia, che la vera soluzione non sia quella di usare ddma invece optare per qualcosa che è progettato per funzionare al contrario ddrescue. Ho descritto un modo per farlo in una risposta.
Starfry,

1
@starfry: certo, un programma che lo farà sarà una bella soluzione. Tuttavia non sono affatto sicuro di ddrescuequi. Non se si aspetta che funzioni su diversi dispositivi e devi ingannarlo per accettare i tuoi argomenti. Potrebbe non avere nemmeno la proprietà "riempimento minimo del buffer" internamente (poiché non è necessario con dispositivi diversi), quindi potrebbe danneggiare i dati. Dovresti controllare nel codice sorgente se è effettivamente progettato per il tuo caso d'uso.
frostschutz,

1

Stai leggendo 4096 blocchi e quindi scrivendo quei 4096 blocchi nei successivi 4096 blocchi del disco, sovrascrivendo così i secondi 4096 blocchi prima che possano essere letti. Devi leggere 8129 blocchi per ottenere quei secondi 4096 prima di iniziare qualsiasi scrittura, e quindi devi solo scrivere 4096 blocchi prima di leggere il successivo 4096.

Non hai menzionato che tipo di filesystem sia. Se è ext [234] e hai una versione recente di e2fsprogs, puoi usare e2image -ra -O 512 /dev/sdj2. Questo ha anche il vantaggio di essere abbastanza intelligente da saltare lo spazio libero nel volume.


Questo ha senso quando lo leggo e ho intenzione di dare un altro sguardo basato su quello. Ma non spiega perché ha funzionato sul file di test.
Starfry,

Per quanto riguarda il filesystem, ti riferisci al filesystem che contiene il mio file di test? Questo è, ext4ma per la copia del dispositivo a blocchi, qualsiasi filesystem dovrebbe essere irrilevante.
Starfry,

@starfry, l'unico modo che conosco per farlo in modo generico è usare l'algoritmo suggerito da Emmanuel (lavorare all'indietro dalla fine), che è ciò che fa gparted.
psusi,

per quanto riguarda la dimensione del blocco, avevo provato blocchi più grandi (avrei dovuto scriverlo nella domanda). Ho scoperto che non è diventato più affidabile nemmeno un buffer di settore 64 KB. La soluzione affidabile è quella di correre all'indietro, cosa che ddnon funziona.
Starfry,

1

Una soluzione affidabile richiede di assicurarsi che nulla scriva in un'area che potrebbe non essere stata letta e l'unico modo reale per ottenere ciò è eseguire la copia in direzione inversa.

Lo ddrescuestrumento può funzionare in direzione opposta ma rifiuta di funzionare con input e output identici. Tuttavia è possibile ingannarlo duplicando il nodo del dispositivo.

Ho eseguito alcuni esperimenti rapidi e sembra funzionare. La riga di comando è:

$ ddrescue -f -R -s 200704s -o 4096s /dev/sdj11 /dev/sdj11_copy

Gli argomenti sono

  • -f è richiesto per forzarlo a scrivere su un dispositivo di output esistente
  • -R gli dice di funzionare in direzione opposta
  • -sindica la quantità di input da copiare (ho usato il ssuffisso per specificare il numero di settori)
  • -ogli dice di cercare in avanti nel dispositivo di output prima di scrivere (specificato di nuovo nei settori con il ssuffisso)
  • /dev/sdj11 è il dispositivo a blocchi da leggere
  • /dev/sdj11_copy è il dispositivo a blocchi da scrivere

Ho creato /dev/sdj11_copycon mknodper abbinare i parametri di /dev/sdj11.

Ho fatto solo alcuni test molto rapidi ma questo sembra funzionare bene per copiare un dispositivo non elaborato. Non funziona su un file (non potrei indurlo ad andare oltre i file essendo lo stesso)

Questo non risponde alla mia domanda originale che mi ha chiesto come raggiungere questo obiettivo, ddma penso che, avendo letto le altre risposte, la risposta è che ddnon è possibile farlo.


Cosa succede se ddrescuescopre un blocco errato in questo scenario? Se salta su un'altra area del disco (per evitare blocchi danneggiati) e continua a copiare da lì, sovrascriverà di nuovo parti non ancora copiate dei dati. Se non prevede di funzionare con lo stesso dispositivo, non ha motivo di adottare misure speciali per prevenire vari possibili casi di corruzione dei dati.
frostschutz,

Sono d'accordo che questo è un potenziale problema, ma non ho esaminato i casi limite, poiché sono stato in grado di usarlo per fare ciò di cui avevo bisogno. Ci sono ddrescueopzioni per limitare i suoi tentativi di recuperare dati errati, ma non ho esaminato come usarli.
Starfry,

Il fatto che si rifiuti di funzionare se l'ingresso e l'uscita sono uguali è probabilmente una buona indicazione del fatto che non è sicuro.
psusi,
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.