Compressione del flusso al volo che non si riversa in risorse hardware?


23

Ho 200 GB di spazio libero su disco, 16 GB di RAM (di cui ~ 1 GB occupati dal desktop e dal kernel) e 6 GB di swap.

Ho un SSD esterno da 240 GB, con 70 GB utilizzati 1 e il resto libero, di cui ho bisogno per eseguire il backup sul mio disco.

Normalmente, prima vorrei dd if=/dev/sdb of=Desktop/disk.imgil disco e poi comprimerlo, ma fare prima l'immagine non è un'opzione poiché farlo richiederebbe molto più spazio su disco di quello che ho, anche se il passaggio di compressione comporterà la compressione dello spazio libero in modo che il l'archivio finale può facilmente adattarsi al mio disco.

ddscrive su STDOUT per impostazione predefinita e gzippuò leggere da STDIN, quindi in teoria posso scrivere dd if=/dev/sdb | gzip -9 -, ma gzipimpiega molto più tempo a leggere i byte di ddquanti ne possano produrre.

Da man pipe:

I dati scritti all'estremità di scrittura della pipe vengono bufferizzati dal kernel fino a quando non vengono letti dall'estremità di lettura della pipe.

Visualizzo a |come un vero pipe - un'applicazione che inserisce i dati e l'altra che estrae i dati dalla coda del pipe il più rapidamente possibile.

Cosa succede quando il programma sul lato sinistro scrive più dati più rapidamente di quanto l'altro lato della pipe possa sperare di elaborarlo? Provocherà un utilizzo estremo della memoria o dello scambio o il kernel tenterà di creare un FIFO su disco, riempiendo così il disco? O fallirà SIGPIPE Broken pipese il buffer è troppo grande?

Fondamentalmente, questo si riduce a due domande:

  1. Quali sono le implicazioni e i risultati di inserire più dati in una pipe di quanti ne vengano letti alla volta?
  2. Qual è il modo affidabile per comprimere un flusso di dati su disco senza mettere l'intero disco di dati non compresso sul disco?

Nota 1: non posso semplicemente copiare esattamente i primi 70 GB usati e mi aspetto di ottenere un sistema o un filesystem funzionante, a causa della frammentazione e di altre cose che richiedono che il contenuto completo sia intatto.


Perché eseguire il backup di un intero file system come quello, anziché solo le directory degli utenti e forse un elenco di software non standard installato?
jamesqf,

5
@jamesqf Eg. perché è molto più facile da ripristinare ...
deviantfan,

4
@jamesqf Perché poi ottengo anche il settore di avvio e la partizione di swap, in modo da poter ricreare esattamente il disco invece di avere un miliardo di file fastidiosi.
cat

3
Suggerimento casuale: guarda lzopinvece di gzip; si comprime molto più velocemente con solo un rapporto di compressione leggermente inferiore. Lo trovo ideale per le immagini del disco in cui la velocità di compressione può essere un vero collo di bottiglia.
marcelm,

1
"Cosa succede quando il programma sul lato sinistro scrive più dati più rapidamente di quanto l'altro lato del tubo possa sperare di elaborarlo?" Il kernel farà sospendere il processo di scrittura fino a quando non ci sarà più spazio nella pipe.
Tavian Barnes,

Risposte:


16

Tecnicamente non hai nemmeno bisogno di dd:

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

Se fate uso dd, si dovrebbe sempre andare con i più grandi del blocco di default come dd bs=1Mo subire l'inferno syscall ( dd's-blocco di default è 512 byte, in quanto read()S e write()s che è in 4096chiamate di sistema per MiB, troppo in alto).

gzip -9usa MOLTA CPU in più con pochissimo da mostrare. Se ti gzipsta rallentando, abbassa il livello di compressione o usa un metodo di compressione diverso (più veloce).

Se stai eseguendo backup basati su file anziché ddimmagini, potresti avere una logica che decide se comprimere o meno (non ha senso farlo per vari tipi di file). dar( taralternativa`) è un esempio che ha opzioni per farlo.

Se il tuo spazio libero è ZERO (perché è un SSD che restituisce in modo affidabile zero dopo TRIM e hai eseguito fstrime rilasciato cache) puoi anche usare ddcon conv=sparseflag per creare un'immagine sparsa, non montabile in loop, montabile e che utilizza zero spazio su disco per le aree zero . Richiede che il file di immagine sia supportato da un filesystem che supporti file sparsi.

In alternativa per alcuni filesystem esistono programmi in grado di visualizzare solo le aree utilizzate.


1
"Se usi dd, dovresti sempre andare con blocchi di dimensioni superiori a quelle predefinite come dd bs=1M" - Puoi, ma non aspettarti troppo. Sul mio PC, ddfarà circa 2 GB / s con blocchi da 512 byte. Questo non sarà il collo di bottiglia; gzipsarà.
marcelm,

@marcelm Non sappiamo mai che tipo di macchine stanno usando le persone. Se stai ddandando a 2 GB / s con blocchi da 512 byte, sarei sorpreso se non riuscisse a massimizzare al massimo un core della CPU al 100% nel processo. Ora se la tua casella è un quadcore che rimane comunque inattivo, potresti non notare una differenza. Tutti gli altri lo fanno ancora, comunque.
frostschutz,

9
Sospiro. Ogni volta che ddviene menzionata la dimensione del blocco, la gente viene a fare pipì. gzipanche essere cpu intensivo faceva parte della mia risposta, ok? E scusa, non sono d'accordo con "trascurabile". Potrebbe aggiungere solo 1-2 secondi per concerto gzip -9(ma ciò equivale comunque a minuti durante l'elaborazione di centinaia di concerti) ma seguendo i tuoi consigli con lzop -11s per concerto contro 4s per concerto. Testato su una patata (vserver single core). L'aggiunta di una dimensione di blocco sana ddnon costa nulla e ha zero aspetti negativi. Non fare il pignolo. Fallo e basta. ymmv
frostschutz,

19

ddlegge e scrive i dati un blocco alla volta e ha sempre un blocco in sospeso. Così

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

mostra che ddutilizza circa 1 MB di memoria. Puoi giocare con la dimensione del blocco e rilasciarlo valgrindper vedere l'effetto sulla ddvelocità.

Quando entri in gioco gzip, ddsemplicemente rallenta per adattarsi alla gzipvelocità. Il suo utilizzo della memoria non aumenta, né fa sì che il kernel memorizzi i buffer sul disco (il kernel non sa come farlo, tranne tramite swap). Un tubo rotto si verifica solo quando una delle estremità del tubo muore; vedi signal(7)e write(2)per i dettagli.

così

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

è un modo sicuro per fare ciò che cerchi.

Durante il piping, il processo di scrittura finisce per essere bloccato dal kernel se il processo di lettura non tiene il passo. Puoi vederlo correndo

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

Vedrai che ddlegge 1 MB, quindi emette un write()che rimane lì in attesa di un minuto mentre sleepcorre. Ecco come entrambi i lati di una pipe si bilanciano: i blocchi del kernel scrivono se il processo di scrittura è troppo veloce e bloccano le letture se il processo di lettura è troppo veloce.


1
È abbastanza bello. Con quale meccanismo ddsa rallentare per adeguare gzipla velocità? È automatico, come dal kernel, o calcola dai metadati sul descrittore del file di output?
gatto,

9
@cat È automatico; ddchiama write()per inserire i dati nella pipe. write()trasferisce effettivamente il controllo al kernel in modo che possa manipolare la memoria della pipe. Se il kernel vede che la pipe è piena, attenderà ("blocco") fino a quando la pipe avrà spazio sufficiente. Solo allora la write()chiamata termina e trasferisce nuovamente il controllo dd, che scriverà nuovamente i dati nella pipe.
marcelm,

9

Non ci sono implicazioni negative oltre alle prestazioni: la pipe ha un buffer, che di solito è 64 KB, e successivamente, una scrittura sulla pipe semplicemente bloccherà fino a quando non gziplegge altri dati.


8

Rispondere alla domanda reale su come funziona: "che cosa succede se il programma sul lato sinistro scrive più dati più rapidamente di quanto l'altro lato del tubo possa sperare di elaborarlo?"

Questo non succede. C'è un buffer abbastanza piccolo, di dimensioni limitate nel tubo; vedi Quanto è grande il buffer del tubo?

Quando il buffer delle pipe è pieno, il programma di invio si blocca . Quando effettua una chiamata di scrittura, il kernel non restituirà il controllo al programma finché i dati non sono stati scritti nel buffer. Questo dà al programma di lettura il tempo della CPU in cui svuotare il buffer.


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.