¿Cuándo es adecuado dd para copiar datos? (o, cuando se lee () y escribe () parcial)


60

Versión corta: ¿En qué circunstancias es ddseguro usar para copiar datos, lo que significa que no hay riesgo de corrupción debido a una lectura o escritura parcial?

Versión larga - preámbulo: a dd menudo se usa para copiar datos, especialmente desde o hacia un dispositivo ( ejemplo ). A veces se le atribuyen propiedades místicas de poder acceder a dispositivos en un nivel más bajo que otras herramientas (cuando en realidad es el archivo del dispositivo el que está haciendo la magia), pero dd if=/dev/sdaes lo mismo cat /dev/sda. dda veces se piensa que es más rápido, pero catpuede vencerlo en la práctica . Sin embargo, ddtiene propiedades únicas que lo hacen realmente útil a veces .

Problema: dd if=foo of=bar no es, de hecho, lo mismo que cat <foo >bar. En la mayoría de las unidades¹, ddrealiza una sola llamada a read(). (Encuentro POSIX borroso en lo que constituye "leer un bloque de entrada" en dd). Si read()devuelve un resultado parcial (que, según POSIX y otros documentos de referencia, está permitido a menos que la documentación de implementación indique lo contrario), se copia un bloque parcial. Exactamente el mismo problema existe para write().

Observaciones : en la práctica, he descubierto que ddpuede hacer frente a dispositivos de bloque y archivos normales, pero puede ser que no lo haya ejercitado demasiado. Cuando se trata de tuberías, no es difícil ddculpar; por ejemplo prueba este código :

yes | dd of=out bs=1024k count=10

y verifique el tamaño del outarchivo (es probable que sea muy inferior a 10 MB).

Pregunta : ¿En qué circunstancias es ddseguro usar para copiar datos? En otras palabras, ¿qué condiciones en los tamaños de bloque, en la implementación, en los tipos de archivo, etc., pueden garantizar que ddse copiarán todos los datos?

( GNU dd tiene una fullblockbandera para decirle que llame read()o write()en un bucle para transferir un bloque completo. Por dd iflag=fullblocklo tanto, siempre es seguro. Mi pregunta es sobre el caso cuando estas banderas (que no existen en otras implementaciones) no se usan .)

Checked He comprobado OpenBSD, GNU coreutils y BusyBox.


Nunca he visto ningún sistema Unixy que realmente pudiera leer algunos MiB en una sola lectura (2) ...
vonbrand

3
Cuando se usa count, iflag=fullblockes obligatorio (o, alternativamente, iflag=count_bytes). No hay ninguna oflag=fullblock.
frostschutz

Respuestas:


10

De la especificación :

  • Si bs=exprse especifica el operando y no se solicitan conversiones que no sean sync, noerroro notruncse soliciten, los datos devueltos de cada bloque de entrada se escribirán como un bloque de salida separado; Si el read()retorno es inferior a un bloque completo y syncno se especifica la conversión, el bloque de salida resultante será del mismo tamaño que el bloque de entrada.

Entonces esto es probablemente lo que causa tu confusión. Sí, debido a que ddestá diseñado para el bloqueo, de manera predeterminada, los read()s parciales se asignarán 1: 1 a write()s parciales , o bien se syncdotarán de NUL de relleno de cola o caracteres de espacio bs=cuando conv=syncse especifique.

Esto significa que ddes seguro de usar para copiar datos (sin riesgo de corrupción debido a una lectura o escritura parcial) en todos los casos, excepto uno en el que está limitado arbitrariamente por un count=argumento, porque de lo contrario ddfelizmente write()su salida en bloques de tamaño idéntico a aquellos en los que su entrada fue read()hasta que read()la atravesó por completo. E incluso esta advertencia es sólo es cierto cuando bs=se especifica o obs=se no se especifica, como el siguiente oración en los estados de especificaciones:

  • Si bs=exprno se especifica el operando , o si se solicita una conversión diferente a sync, noerroro notruncse solicita, la entrada se procesará y se recopilará en bloques de salida de tamaño completo hasta llegar al final de la entrada.

Sin ibs=y / o obs=argumentos esto no puede importa - porque ibsy obsson a la vez el mismo tamaño por defecto. Sin embargo, puede ser explícito sobre el almacenamiento en búfer de entrada especificando diferentes tamaños para cualquiera y no especificando bs= (porque tiene prioridad) .

Por ejemplo, si haces:

IN| dd ibs=1| OUT

... entonces un POSIX lo ddhará write()en fragmentos de 512 bytes al recopilar cada read()byte individualmente en un solo bloque de salida.

De lo contrario, si lo haces ...

IN| dd obs=1kx1k| OUT

... un POSIX ddtendrá read() un máximo de 512 bytes a la vez, pero write()cada bloque de salida de un tamaño de megabyte (el kernel permite y exceptúa posiblemente el último, porque eso es EOF) completo al recopilar la entrada en bloques de salida de tamaño completo .

Sin embargo, también de la especificación:

  • count=n
    • Copie solo n bloques de entrada.

count=asigna a i?bs=bloques, por lo que para manejar un límite arbitrario de forma count=portátil necesitarás dos dds. La forma más práctica de hacerlo con dos dds es canalizando la salida de uno en la entrada de otro, lo que seguramente nos coloca en el ámbito de la lectura / escritura de un archivo especial independientemente del tipo de entrada original.

Una tubería IPC significa que al especificar [io]bs=argumentos que, para hacerlo de manera segura, debe mantener dichos valores dentro del PIPE_BUFlímite definido del sistema . POSIX establece que el núcleo del sistema sólo debe garantizar atómicas read()s y write()s dentro de los límites de PIPE_BUFcomo se define en limits.h. POSIX garantiza que PIPE_BUFsea al menos ...

  • {_POSIX_PIPE_BUF}
    • Número máximo de bytes que se garantiza que son atómicos cuando se escribe en una tubería.
    • Valor: 512

... (que también es el ddtamaño de bloque de E / S predeterminado ) , pero el valor real suele ser al menos 4k. En un sistema Linux actualizado es, por defecto, 64k.

Entonces, cuando configura sus ddprocesos, debe hacerlo en un factor de bloque basado en tres valores:

  1. bs = (obs = PIPE_BUFo menor)
  2. n = número total deseado de bytes leídos
  3. cuenta = n / bs

Me gusta:

yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s

Tiene que sincronizar i / ow / ddpara manejar entradas no buscables. En otras palabras, haga explícitos los amortiguadores de tubería y dejarán de ser un problema. Para eso ddes eso . La cantidad desconocida aquí es yesel tamaño del búfer, pero si bloquea eso a una cantidad conocida con otra, ddentonces una pequeña multiplicación informada puede ser dd segura de usar para copiar datos (sin riesgo de corrupción debido a una lectura o escritura parcial) incluso cuando limite arbitrariamente la entrada con count=cualquier tipo de entrada arbitraria en cualquier sistema POSIX y sin perder un solo byte.

Aquí hay un fragmento de la especificación POSIX :

  • ibs=expr
    • Especifique el tamaño del bloque de entrada, en bytes, por (el valor predeterminado es 512) .expr
  • obs=expr
    • Especifique el tamaño del bloque de salida, en bytes, por (el valor predeterminado es 512) .expr
  • bs=expr
    • Establezca los tamaños de bloque de entrada y salida en exprbytes, reemplazando ibs=y obs=. Si no se especifica otra conversión que sync, noerrory notruncse especifica, cada bloque de entrada se copiará a la salida como un bloque único sin agregar bloques cortos.

También encontrarás algo de esto mejor explicado aquí .


5

Con sockets, tuberías o ttys, read () y write () pueden transferir menos del tamaño solicitado, por lo que cuando se usa dd en estos, necesita el indicador de bloque completo. Sin embargo, con los archivos normales y los dispositivos de bloqueo, solo hay dos veces cuando pueden hacer una lectura / escritura corta: cuando llega a EOF, o si hay un error. Esta es la razón por la cual las implementaciones anteriores de dd sin el indicador de bloque completo eran seguras de usar para la duplicación de disco.


¿Es eso cierto en todos los universos modernos? (Sé que no era cierto para Linux en algún momento, posiblemente hasta 2.0.xo 2.2.x. Recuerdo que mke2fsfalló en silencio porque llamó write()con un tamaño no potenciado de 2 (3kB IIRC) y el núcleo redondeado hasta un poder de 2.)
Gilles 'SO- deja de ser malvado'

@Gilles que suena como un problema completamente diferente. Siempre debe usar un múltiplo del tamaño de bloque adecuado con los dispositivos de bloque. Estoy bastante seguro de que es cierto para todas las unicies, y también es cierto para Windows.
psusi

Además de las cintas, el tamaño de bloque de un dispositivo es puramente para que el núcleo se preocupe o no. cat </dev/sda >/dev/sdbfunciona bien para clonar un disco.
Gilles 'SO- deja de ser malvado'

@Gilles eso se debe a que cat usa el tamaño de bloque apropiado, como OrbWeaver señaló en su respuesta.
psusi

No, no hay un "tamaño de bloque apropiado". catselecciona un tamaño de búfer para el rendimiento; no obtiene ninguna información relacionada con el dispositivo del núcleo. Aparte de las cintas, se puede read()y write()a un dispositivo de bloque con cualquier tamaño. Al menos en Linux, st_blksizedepende solo del sistema de archivos donde se encuentra el inodo del dispositivo de bloque, no del dispositivo subyacente.
Gilles 'SO- deja de ser malvado'
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.