¿Compresión de flujo sobre la marcha que no se extiende a los recursos de hardware?

23

Tengo 200 GB de espacio libre en disco, 16 GB de RAM (de los cuales ~ 1 GB está ocupado por el escritorio y el núcleo) y 6 GB de intercambio.

Tengo un SSD externo de 240 GB, con 70 GB utilizados 1 y el resto libre, que necesito hacer una copia de seguridad en mi disco.

Normalmente, dd if=/dev/sdb of=Desktop/disk.imgprimero usaría el disco y luego lo comprimiría, pero hacer la imagen primero no es una opción, ya que hacerlo requeriría mucho más espacio en el disco que el que tengo, aunque el paso de compresión provocará que el espacio libre se aplaste, por lo que El archivo final puede caber fácilmente en mi disco.

ddescribe en STDOUT de forma predeterminada y gzippuede leer desde STDIN, por lo que, en teoría, puedo escribir dd if=/dev/sdb | gzip -9 -, pero gziptarda mucho más en leer los bytes que ddpuede producirlos.

De man pipe:

Los datos escritos en el extremo de escritura de la tubería se almacenan en el núcleo hasta que se leen desde el extremo de lectura de la tubería.

Visualizo un | como una tubería real: una aplicación introduce datos y la otra saca datos de la cola de la tubería lo más rápido posible.

¿Qué sucede cuando el programa del lado izquierdo escribe más datos más rápido de lo que el otro lado de la tubería puede esperar procesar? ¿Causará un uso extremo de memoria o intercambio, o el núcleo intentará crear un FIFO en el disco, llenando así el disco? ¿O simplemente fallará SIGPIPE Broken pipesi el búfer es demasiado grande?

Básicamente, esto se reduce a dos preguntas:

  1. ¿Cuáles son las implicaciones y los resultados de insertar más datos en una tubería de los que se leen a la vez?
  2. ¿Cuál es la forma confiable de comprimir un flujo de datos en el disco sin poner todo el flujo de datos sin comprimir en el disco?

Nota 1: No puedo simplemente copiar exactamente los primeros 70 GB usados ​​y esperar obtener un sistema o sistema de archivos que funcione, debido a la fragmentación y otras cosas que requerirán que el contenido completo esté intacto.

gato
fuente
¿Por qué haría una copia de seguridad de un sistema de archivos completo como ese, en lugar de solo directorios de usuarios, y tal vez una lista de software no estándar que está instalado?
jamesqf
55
@jamesqf Eg. porque es mucho más fácil de restaurar ...
deviantfan
44
@jamesqf Porque entonces también obtengo el sector de arranque y la partición de intercambio, para poder recrear el disco exactamente en lugar de tener mil millones de archivos molestos.
gato
3
Consejo aleatorio: mirar en lzoplugar de gzip; se comprime mucho más rápido con solo una relación de compresión ligeramente inferior. Lo encuentro ideal para imágenes de disco donde la velocidad de compresión puede ser un verdadero cuello de botella.
marcelm
1
"¿Qué sucede cuando el programa del lado izquierdo escribe más datos más rápido de lo que el otro lado de la tubería puede esperar procesar?" El núcleo hará que el proceso de escritura se suspenda hasta que haya más espacio en la tubería.
Tavian Barnes

Respuestas:

16

Técnicamente ni siquiera necesitas dd:

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

Si haces uso dd, siempre hay que ir con más grande que tamaño de bloque por defecto como dd bs=1Mo sufrir demonios syscall ( dd's de bloque por defecto es de 512 bytes, ya que read()S y write()s eso es 4096llamadas al sistema por MiB, el exceso de gastos generales).

gzip -9usa MUCHO más CPU con muy poco para mostrar. Sigzip está ralentizando, baje el nivel de compresión o use un método de compresión diferente (más rápido).

Si está haciendo copias de seguridad basadas en archivos en lugar de ddimágenes, podría tener cierta lógica que decida si comprimir o no (no tiene sentido hacerlo para varios tipos de archivos). dar(tar alternativa`) es un ejemplo que tiene opciones para hacerlo.

Si su espacio libre es CERO (porque es un SSD que devuelve cero de manera confiable después de TRIM y ejecutó fstrimy soltó cachés), también puede usarlo ddcon una conv=sparsebandera para crear una imagen escasa, sin comprimir, montable en bucle que usa cero espacio en disco para las áreas cero . Requiere que el archivo de imagen esté respaldado por un sistema de archivos que admita archivos dispersos.

Alternativamente, para algunos sistemas de archivos existen programas que solo pueden generar imágenes de las áreas utilizadas.

Frostschutz
fuente
1
"Si usas dd, siempre debes ir con un tamaño de bloque mayor que el predeterminado como dd bs=1M" - Puedes, pero no esperes demasiado. En mi PC, ddharé unos 2GB / s con bloques de 512 bytes. Ese no será el cuello de botella; gzipestarán.
marcelm
@marcelm Nunca sabemos qué tipo de máquina utilizan las personas. Si tiene dd2GB / s con bloques de 512 bytes, me sorprendería si no maximizara un núcleo de CPU al 100% en el proceso. Ahora, si su caja es un quadcore que de todos modos permanece inactivo, es posible que no note la diferencia. Sin embargo, todos los demás todavía lo hacen.
frostschutz
99
Suspiro. Cada vez que ddse menciona el tamaño de bloque, la gente viene a buscar cosas. gzipser intensivo en CPU también fue parte de mi respuesta, ¿de acuerdo? Y lo siento, no estoy de acuerdo con "insignificante". Es posible que solo agregue 1-2s por concierto gzip -9(pero eso aún equivale a minutos cuando se procesan cientos de conciertos), pero siguiendo su consejo lzop -1es 1s por concierto versus 4s por concierto. Probado en una patata (servidor de un solo núcleo). Agregar un tamaño de bloque sano ddno cuesta nada y tiene cero inconvenientes. No hagas trampas. Simplemente hazlo. ymmv
frostschutz
19

ddlee y escribe datos un bloque a la vez, y solo tiene un bloque pendiente. Asi que

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

muestra que ddusa aproximadamente 1 MB de memoria. Puedes jugar con el tamaño del bloque y soltarlo valgrindpara ver el efecto endd la velocidad.

Cuando entras gzip, ddsimplemente disminuye la velocidad para que coincidagzip la velocidad. Su uso de memoria no aumenta, ni hace que el kernel almacene los búferes en el disco (el kernel no sabe cómo hacerlo, excepto mediante intercambio). Una tubería rota solo ocurre cuando uno de los extremos de la tubería muere; ver signal(7)ywrite(2) para más detalles.

Así

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

es una forma segura de hacer lo que buscas.

Al realizar la canalización, el proceso de escritura termina siendo bloqueado por el núcleo si el proceso de lectura no se mantiene. Puedes ver esto ejecutando

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

Verá que ddlee 1 MB, luego emite un write()mensaje que se sleepqueda allí esperando un minuto mientras se ejecuta. Así es como se equilibran ambos lados de una tubería: el núcleo bloquea la escritura si el proceso de escritura es demasiado rápido, y bloquea las lecturas si el proceso de lectura es demasiado rápido.

Stephen Kitt
fuente
1
Eso es muy bonito. ¿Por qué mecanismo ddsabe reducir la velocidad para igualar gzipla velocidad? Es automático, como el núcleo, ¿o se calcula a partir de metadatos sobre su descriptor de archivo de salida?
gato
99
@cat Es automático; ddllamadas write()para poner datos en la tubería. write()en realidad transfiere el control al kernel para que pueda manipular la memoria de la tubería. Si el núcleo ve que la tubería está llena, esperará ("bloqueará") hasta que la tubería tenga suficiente espacio. Solo entonces write()finalizará la llamada y volverá a transferirse el control dd, que luego volverá a escribir datos en la tubería.
marcelm
9

No hay implicaciones negativas además del rendimiento: la tubería tiene un búfer, que generalmente es de 64K, y después de eso, una escritura en la tubería simplemente se bloqueará hasta que se gziplean más datos.

Ulrich Schwarz
fuente
8

Respondiendo a la pregunta real sobre cómo funciona: "¿y si el programa del lado izquierdo escribe más datos más rápido de lo que el otro lado de la tubería puede esperar procesarlo?"

Esto no pasa Hay un tampón bastante pequeño y de tamaño limitado en la tubería; ver ¿Qué tan grande es el buffer de la tubería?

Una vez que el buffer de la tubería está lleno, el programa de envío se bloquea . Cuando realiza una llamada de escritura, el núcleo no devolverá el control al programa hasta que los datos se hayan escrito en el búfer. Esto le da al programa de lectura tiempo de CPU para vaciar el búfer.

pjc50
fuente