Versión corta: ¿En qué circunstancias es dd
seguro de usar para copiar datos, ¿seguro significa que no hay riesgo de corrupción debido a una lectura o escritura parcial?
Versión larga — preámbulo: dd
se usa a menudo para copiar datos, especialmente desde o hacia un dispositivo (ejemplo). A veces se le atribuyen propiedades místicas de poder acceder a dispositivos a un nivel más bajo que otras herramientas (cuando en realidad es el archivo del dispositivo el que hace la magia), pero dd if=/dev/sda
es lo mismo que cat /dev/sda
. dd
a veces se piensa que es más rápido, pero cat
puede vencerlo en la práctica. No obstante, dd
tiene propiedades únicas que lo hacen genuinamente útil a veces.
Problema: dd if=foo of=bar
no es, de hecho, lo mismo que cat <foo >bar
. En la mayoría de los unice¹, dd
hace una sola llamada a read()
. (Encuentro POSIX confuso 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 encontrado que dd
puede hacer frente a dispositivos de bloque y archivos normales, pero eso puede deberse a que no lo he ejercitado mucho. Cuando se trata de tuberías, no es difícil poner dd
en falta; por ejemplo, prueba este código:
yes | dd of=out bs=1024k count=10
y verifique el tamaño del out
archivo (es probable que tenga menos de 10 MB).
Pregunta :¿En qué circunstancias es dd
seguro de usar para copiar datos? En otras palabras, qué condiciones en los tamaños de bloque, en la implementación, en los tipos de archivos, etc., pueden asegurar que dd
copiará todos los datos?
(GNU dd tiene un fullblock
bandera para decirle que llame a read()
o write()
en un bucle para transferir un bloque completo. Entonces dd iflag=fullblock
siempre es seguro. Mi pregunta es sobre el caso en que no se utilizan estas banderas (que no existen en otras implementaciones).
¹
Revisé OpenBSD, GNU coreutils y BusyBox.
Respuesta aceptada:
De la especificación:
- Si
bs=
expr
se especifica el operando y no hay más conversiones quesync
,noerror
, onotrunc
se solicitan, los datos devueltos de cada bloque de entrada se escribirán como un bloque de salida separado; siread()
devuelve menos de un bloque completo ysync
no se especifica la conversión, el bloque de salida resultante tendrá el mismo tamaño que el bloque de entrada.
Entonces esto es probablemente lo que causa su confusión. Sí, porque dd
está diseñado para bloquear, por defecto parcial read()
s se asignará 1:1 a write()
parcial s, o bien sync
d out en el relleno de la cola NUL o caracteres de espacio para bs=
tamaño cuando conv=sync
se especifica.
Esto significa que dd
es seguro de usar para copiar datos (sin riesgo de corrupción debido a una lectura o escritura parcial) en todos los casos menos uno en el que está arbitrariamente limitado por un count=
argumento, porque de lo contrario dd
felizmente write()
su salida en bloques de tamaño idéntico a aquellos en los que su entrada fue read()
hasta que read()
s completamente a través de él. E incluso esta advertencia es solo cierta cuando bs=
se especifica o obs=
es no especificado, como dice la siguiente oración en la especificación:
- Si
bs=
expr
no se especifica el operando, o una conversión que no seasync
,noerror
, onotrunc
se solicita, la entrada se procesará y reunirá en bloques de salida de tamaño completo hasta que se alcance el final de la entrada.
Sin ibs=
y/o obs=
argumentos esto no puede importar, porque ibs
y obs
ambos tienen 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 lo hace:
IN| dd ibs=1| OUT
…entonces un POSIX dd
write()
en fragmentos de 512 bytes recopilando cada uno read()
byte en un único bloque de salida.
De lo contrario, si lo haces...
IN| dd obs=1kx1k| OUT
…un POSIX dd
read()
al máximo 512 bytes a la vez, pero write()
cada bloque de salida del tamaño de un megabyte (permitiendo el núcleo y posiblemente exceptuando el último, porque eso es EOF) en su totalidad mediante la recopilación de entrada en bloques de salida de tamaño completo .
Sin embargo, también de la especificación:
count=n
- Copiar solo n bloques de entrada.
count=
asigna a i?bs=
bloques, y así poder manejar un límite arbitrario en count=
de forma portátil necesitarás dos dd
s. La forma más práctica de hacerlo con dos dd
s es canalizando la salida de uno a la entrada de otro, lo que seguramente nos coloca en el ámbito de leer/escribir un archivo especial independientemente del tipo de entrada original.
Una canalización IPC significa que al especificar [io]bs=
argumenta que, para hacerlo de manera segura, debe mantener dichos valores dentro del PIPE_BUF
definido por el sistema límite. POSIX establece que el kernel del sistema solo debe garantizar read()
atómico s y write()
s dentro de los límites de PIPE_BUF
como se define en limits.h
. POSIX garantiza que PIPE_BUF
ser al menos …
{_POSIX_PIPE_BUF}
- Número máximo de bytes que se garantiza que sean atómicos al escribir en una canalización.
- Valor:512
…(que también resulta ser el dd
predeterminado tamaño de bloque de E/S) , pero el valor real suele ser de al menos 4k. En un sistema linux actualizado es, por defecto, 64k.
Así que cuando configuras tu dd
procesos debes hacerlo en un bloque factor basado en tres valores:
- bs =( obs =
PIPE_BUF
o menos) - n =número total deseado de bytes leídos
- 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
Tienes que sincronizar i/o con dd
para manejar entradas no buscables. En otras palabras, haga explícitos los pipe-buffers y dejarán de ser un problema. Eso es lo que dd
es para. La cantidad desconocida aquí es yes
el tamaño del búfer, pero si lo bloquea a un conocido cantidad con otro dd
entonces una pequeña multiplicación informada puede hacer dd
seguro de usar para copiar datos (sin riesgo de corrupción debido a una lectura o escritura parcial) incluso cuando se limita arbitrariamente la entrada con count=
con cualquier tipo de entrada arbitrario 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, mediante
expr
(el valor predeterminado es 512) .
- Especifique el tamaño del bloque de entrada, en bytes, mediante
obs=
expr
- Especifique el tamaño del bloque de salida, en bytes, mediante
expr
(el valor predeterminado es 512) .
- Especifique el tamaño del bloque de salida, en bytes, mediante
bs=
expr
- Establezca los tamaños de bloque de entrada y salida en
expr
bytes, reemplazandoibs=
yobs=
. Si no hay otra conversión que no seasync
,noerror
ynotrunc
se especifica, cada bloque de entrada se copiará en la salida como un solo bloque sin agregar bloques cortos.
- Establezca los tamaños de bloque de entrada y salida en
También encontrará algo de esto mejor explicado aquí.