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


23

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

Eu tenho um SSD externo de 240 GB, com 70 GB usados 1 e o restante livre, do qual preciso fazer backup no meu disco.

Normalmente, eu faria dd if=/dev/sdb of=Desktop/disk.imgo disco primeiro e depois o compactaria, mas fazer a imagem primeiro não é uma opção, pois isso exigiria muito mais espaço em disco do que eu tenho, mesmo que a etapa de compactação resulte em espaço livre sendo compactado para que o arquivo final pode caber facilmente no meu disco.

ddgrava em STDOUT por padrão e gzippode ler de STDIN, portanto, em teoria, eu posso escrever dd if=/dev/sdb | gzip -9 -, mas gzipleva muito mais tempo para ler bytes do que ddproduzi-los.

De man pipe:

Os dados gravados na extremidade de gravação do pipe são armazenados em buffer pelo kernel até serem lidos na extremidade de leitura do pipe.

Visualizo um |como sendo um tubo real - um aplicativo inserindo dados e o outro retirando dados da fila do tubo o mais rápido possível.

E quando o programa no lado esquerdo grava mais dados mais rapidamente do que o outro lado do canal pode esperar processá-los? Isso causará extrema utilização de memória ou troca, ou o kernel tentará criar um FIFO no disco, preenchendo o disco? Ou apenas falhará SIGPIPE Broken pipese o buffer for muito grande?

Basicamente, isso se resume a duas perguntas:

  1. Quais são as implicações e os resultados de colocar mais dados em um canal do que os lidos 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 simplesmente copiar exatamente os primeiros 70 GB usados ​​e espero obter um sistema ou sistema de arquivos em funcionamento, devido à fragmentação e outras coisas que exigirão que todo o conteúdo esteja intacto.


Por que você faria backup de um sistema de arquivos inteiro como esse, em vez de apenas diretórios do usuário e talvez uma lista de software não padrão instalado?
Jamesqf

5
@jamesqf Eg. porque é muito mais fácil restaurar ... #
301 deviantfan

4
@jamesqf Porque também recebo o setor de inicialização e a partição swap, para poder recriar o disco exatamente em vez de ter um bilhão de arquivos irritantes.
cat

3
Dica aleatória: examine em lzopvez de gzip; ele comprime muito mais rápido com apenas uma taxa de compressão ligeiramente mais baixa. Acho ideal para imagens de disco em que a velocidade de compactação pode ser um gargalo real.
marcelm

1
"O que acontece quando o programa no lado esquerdo grava mais dados mais rapidamente do que o outro lado do canal pode esperar processá-lo?" O kernel fará com que o processo de gravação seja interrompido até que haja mais espaço no tubo.
Tavian Barnes

Respostas:


16

Tecnicamente, você nem precisa de dd:

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

Se você fizer uso dd, você deve sempre ir com maior do que blocksize padrão como dd bs=1Mou sofrer o inferno syscall ( dd's de bloco padrão é de 512 bytes, uma vez que read()s e write()s isso é 4096syscalls por MiB, muita sobrecarga).

gzip -9usa muito mais CPU com muito pouco para mostrar. Se gzipvocê está atrasando, 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 arquivos em vez de ddimagens, poderá ter alguma lógica que decida se deseja compactar ou não (não há sentido em fazer isso para vários tipos de arquivos). dar( taralternativa`) é um exemplo que possui opções para isso.

Se o seu espaço livre for ZERO (porque é um SSD que retorna zero de forma confiável após TRIM e você executou fstrime eliminou caches), você também pode usar ddcom conv=sparsesinalizador para criar uma imagem esparsa, descompactável e montável em loop, que usa espaço em disco zero para as áreas zero . Requer que o arquivo de imagem seja apoiado por um sistema de arquivos que suporte arquivos esparsos.

Alternativamente, para alguns sistemas de arquivos, existem programas capazes de exibir apenas as áreas usadas.


1
"Se você usa dd, sempre deve usar tamanhos de bloco maiores que o padrão, como dd bs=1M" - Você pode, mas não espera muito. No meu PC, ddfará cerca de 2 GB / s com blocos de 512 bytes. Esse não será o gargalo; gzipserá.
marcelm

@ marcelm Nunca sabemos que tipo de máquina as pessoas estão usando. Se você ddusa 2 GB / s com blocos de 512 bytes, ficaria surpreso se não atingisse o máximo de 100% de um núcleo de CPU no processo. Agora, se sua caixa é um quadcore que fica ocioso de qualquer maneira, você pode não notar a diferença. Todo mundo ainda faz, no entanto.
Frostschutz

9
Suspiro. Toda vez que o ddtamanho do bloco é mencionado, as pessoas aparecem. gzipser intensivo em CPU também fazia parte da minha resposta, ok? E desculpe, eu discordo de "insignificante". Ele pode adicionar apenas 1-2s por show com gzip -9(mas isso ainda equivale a minutos ao processar centenas de shows), mas seguindo seu conselho lzop -1, é 1s por show vs. 4s por show. Testado em uma batata (vserver single core). A adição de um tamanho de bloco saudável ddnão custa nada e tem zero desvantagens. Não escolha. Apenas faça. ymmv
frostschutz

19

ddlê e grava dados um bloco de cada vez e só possui um bloco pendente. tão

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

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

Quando você entra gzip, ddsimplesmente diminui a velocidade para corresponder gzipà velocidade. 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 por troca). Um cano quebrado só acontece quando uma das extremidades do cano morre; veja signal(7)e write(2)para detalhes.

portanto

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

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

Ao canalizar, o processo de gravação acaba sendo bloqueado pelo kernel, se o processo de leitura não estiver em andamento. Você pode ver isso executando

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

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


1
Isso é bem legal. Por qual mecanismo ddsabe diminuir a velocidade para corresponder gzipà velocidade? É automático, como no kernel, ou calcula a partir de metadados sobre seu descritor de arquivo de saída?
cat

9
@cat É automático; ddchamadas write()para colocar dados no canal. write()na verdade transfere o controle para o kernel para que ele possa manipular a memória do pipe. Se o kernel vê que o tubo está cheio, ele espera ("bloco") até que o tubo tenha espaço suficiente. Somente então a write()chamada terminará e transferirá o controle de volta para dd, que gravará dados no canal novamente.
marcelm

9

Não há implicações negativas além do desempenho: o canal possui um buffer, que geralmente é de 64 K; depois disso, uma gravação no canal simplesmente bloqueia até gzipler mais alguns dados.


8

Respondendo à pergunta real de como funciona: "e se o programa no lado esquerdo gravar mais dados mais rapidamente do que o outro lado do canal pode esperar processá-los?"

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

Quando o buffer do pipe estiver cheio, o programa de envio será bloqueado . Quando faz uma chamada de gravação, o kernel não retornará o controle ao programa até que os dados tenham sido gravados no buffer. Isso fornece ao CPU o programa de leitura para esvaziar o buffer.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.