Compactação de fluxo on-the-fly que não transborda para recursos de hardware?

21

Eu tenho 200 GB de espaço livre em disco, 16 GB de RAM (dos quais ~ 1 GB é ocupado pelo desktop e kernel) e 6 GB de swap.

Eu tenho um SSD externo de 240 GB, com 70 GB usados 1 e o restante livre, que eu preciso fazer backup em meu disco.

Normalmente, eu copiava o disco dd if=/dev/sdb of=Desktop/disk.img primeiro e compactava-o, mas fazer a imagem primeiro não é uma opção, pois isso exigiria muito mais espaço em disco do que eu, embora a etapa de compactação resulte na espaço livre sendo espremido para que o arquivo final possa caber facilmente no meu disco.

dd escreve para STDOUT por padrão, e gzip pode ler STDIN, então, em teoria, eu posso escrever dd if=/dev/sdb | gzip -9 - , mas gzip demora muito mais para ler bytes do que dd pode produzi-los. / p>

Em man pipe :

Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe.

Eu visualizo um | como sendo um pipe real - um aplicativo envia dados e o outro coleta dados da fila do pipe o mais rápido possível.

O que quando o programa no lado esquerdo escreve mais dados mais rapidamente do que o outro lado do pipe pode esperar processá-lo? Isso causará extrema memória ou uso de swap, ou o kernel tentará criar um FIFO em disco, preenchendo assim o disco? Ou apenas falhará com SIGPIPE Broken pipe se o buffer for muito grande?

Basicamente, isso se resume a duas perguntas:

  1. Quais são as implicações e os resultados de enviar mais dados para um canal do que lê de cada vez?
  2. Qual é a maneira confiável de compactar um fluxo de dados em disco sem colocar todo o fluxo de dados não compactado no disco?

Nota 1: Não posso copiar exatamente os primeiros 70 GB usados e esperar obter um sistema de trabalho ou sistema de arquivos, por causa da fragmentação e outras coisas que exigirão que o conteúdo completo esteja intacto.

    
por cat 18.06.2017 / 15:22

5 respostas

16

Tecnicamente, você nem precisa do dd :

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

Se você usar dd , você deve sempre usar blocos maiores que o padrão como dd bs=1M ou sofrer o inferno syscall (o tamanho padrão do dd é 512 bytes, pois read() s e write() s que é 4096 syscalls por MiB , muita sobrecarga).

gzip -9 usa muito mais CPU com muito pouco para mostrar. Se gzip estiver atrasando você, diminua o nível de compactação ou use um método de compactação diferente (mais rápido).

Se você estiver fazendo backups baseados em arquivo em vez de dd images, poderá ter alguma lógica que decida se deseja compactar ou não (não faz sentido fazê-lo para vários tipos de arquivo). dar ( tar alternative ') é um exemplo que tem opções para isso.

Se o seu espaço livre for ZERO (porque é um SSD que retorna zero com segurança após TRIM e você executou fstrim e descartou caches), você também pode usar dd com conv=sparse flag para criar um descompactado, montável em loop , imagem esparsa que usa zero espaço em disco para as áreas zero. Requer que o arquivo de imagem seja apoiado por um sistema de arquivos que suporta arquivos esparsos.

Como alternativa, para alguns sistemas de arquivos, existem programas capazes de apenas visualizar as áreas usadas.

    
por 18.06.2017 / 16:54
20

dd lê e grava dados em um bloco por vez, e só tem um bloco pendente. Então

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

mostra que dd usa aproximadamente 1 MB de memória. Você pode brincar com o tamanho do bloco e soltar valgrind para ver o efeito na velocidade de dd .

Quando você canaliza para gzip , dd diminui a velocidade para corresponder à velocidade de gzip . Seu uso de memória não aumenta, nem faz com que o kernel armazene os buffers no disco (o kernel não sabe como fazer isso, exceto via swap). Um cano quebrado só acontece quando uma das extremidades do tubo morre; veja signal(7) e write(2) para detalhes.

Assim

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

é uma maneira segura de fazer o que você procura.

Quando o processo de gravação termina, o processo de gravação é bloqueado pelo kernel, se o processo de leitura não for mantido. Você pode ver isso executando

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

Você verá que dd lê 1 MB e emite um write() , que fica lá aguardando um minuto enquanto sleep é executado. É assim que os dois lados de um tubo se equilibram: o núcleo bloqueia as gravações se o processo de gravação for muito rápido e bloqueia as leituras se o processo de leitura for muito rápido.

    
por 18.06.2017 / 16:48
10

Não há implicações negativas além do desempenho: o pipe tem um buffer, que geralmente é de 64K, e depois disso, uma gravação no pipe simplesmente bloqueará até que gzip leia mais alguns dados.

    
por 18.06.2017 / 16:50
9

Respondendo à pergunta atual sobre como ela funciona: "e se o programa à esquerda escrever mais dados mais rapidamente do que o outro lado do canal pode esperar processá-los?"

Isso não acontece. Há um buffer de tamanho bastante pequeno e limitado no tubo; veja Qual é o tamanho do buffer de tubos?

Quando o buffer do pipe estiver cheio, o programa de envio bloqueia . Quando faz uma chamada de escrita, o kernel não retorna o controle para o programa até que os dados tenham sido gravados no buffer. Isto dá tempo de CPU ao programa de leitura para esvaziar o buffer.

    
por 19.06.2017 / 10:44
4

Talvez você só precise dos arquivos e use o tar. Você pode preencher com zeros os blocos que não contêm o que você quer, alguém já perguntou sobre isso. Limpar o espaço não utilizado com zeros (ext3, ext4)

Então, há pigz , que geralmente é mais rápido que gzip .

    
por 19.06.2017 / 07:25