GNU/Linux >> Tutoriels Linux >  >> Linux

Quand Dd est-il adapté à la copie de données ? (ou, Quand Read() et Write() sont-ils partiels) ?

Version courte : Dans quelles circonstances est dd sûr à utiliser pour copier des données, sûr signifiant qu'il n'y a aucun risque de corruption dû à une lecture ou une écriture partielle ?

Version longue — préambule : dd est souvent utilisé pour copier des données, notamment depuis ou vers un appareil (exemple). On lui attribue parfois des propriétés mystiques de pouvoir accéder aux appareils à un niveau inférieur à d'autres outils (alors qu'en fait c'est le fichier de l'appareil qui fait la magie) - pourtant dd if=/dev/sda est la même chose que cat /dev/sda . dd est parfois considéré comme plus rapide, mais cat peut le battre dans la pratique. Néanmoins, dd a des propriétés uniques qui le rendent parfois vraiment utile.

Problème : dd if=foo of=bar n'est en fait pas la même chose que cat <foo >bar . Sur la plupart des unix¹, dd fait un seul appel à read() . (Je trouve POSIX flou sur ce qui constitue la "lecture d'un bloc d'entrée" dans dd .) Si read() renvoie un résultat partiel (ce qui, selon POSIX et d'autres documents de référence, est autorisé sauf indication contraire dans la documentation d'implémentation), un bloc partiel est copié. Exactement le même problème existe pour write() .

Observations :En pratique, j'ai trouvé que dd peut faire face aux périphériques de bloc et aux fichiers normaux, mais c'est peut-être simplement que je ne l'ai pas beaucoup exercé. En ce qui concerne les tuyaux, il n'est pas difficile de mettre dd en faute; par exemple essayez ce code :

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

et vérifiez la taille du out fichier (il est susceptible d'être bien inférieur à 10 Mo).

Question :Dans quelles circonstances est dd sûr à utiliser pour copier des données ? En d'autres termes, quelles conditions sur les tailles de bloc, sur l'implémentation, sur les types de fichiers, etc, peuvent garantir que dd va copier toutes les données ?

(GNU dd a un fullblock flag pour lui dire d'appeler read() ou write() en boucle afin de transférer un bloc complet. Donc dd iflag=fullblock est toujours en sécurité. Ma question concerne le cas où ces drapeaux (qui n'existent pas sur d'autres implémentations) ne sont pas utilisés.)

¹
J'ai vérifié sur OpenBSD, GNU coreutils et BusyBox.

Réponse acceptée :

De la spécification :

  • Si le bs= expr l'opérande est spécifié et aucune conversion autre que sync , noerror , ou notrunc sont demandées, les données renvoyées par chaque bloc d'entrée doivent être écrites dans un bloc de sortie séparé ; si le read() renvoie moins d'un bloc complet et le sync la conversion n'est pas spécifiée, le bloc de sortie résultant doit être de la même taille que le bloc d'entrée.

C'est donc probablement ce qui cause votre confusion. Oui, car dd est conçu pour le blocage, par défaut read() partiel s sera mappé 1:1 sur write() partiel s, ou bien sync d out on tail padding NUL ou espace chars à bs= taille lorsque conv=sync est spécifié.

Cela signifie que dd est sûr à utiliser pour copier des données (sans risque de corruption due à une lecture ou une écriture partielle) dans tous les cas sauf un où il est arbitrairement limité par un count= argument, car sinon dd se fera un plaisir de write() sa sortie dans des blocs de taille identique à ceux dans lesquels son entrée était read() jusqu'à ce qu'il read() s complètement à travers elle. Et même cette mise en garde n'est que vraie quand bs= est spécifié ou obs= n'est pas spécifié, comme l'indique la toute prochaine phrase de la spécification :

  • Si le bs= expr l'opérande n'est pas spécifié, ou une conversion autre que sync , noerror , ou notrunc est demandée, l'entrée doit être traitée et collectée dans des blocs de sortie de taille normale jusqu'à ce que la fin de la saisie soit atteinte.
En relation :Debian – perte récurrente de connectivité sans fil ?

Sans ibs= et/ou obs= arguments cela n'a pas d'importance - parce que ibs et obs sont tous les deux de la même taille par défaut. Cependant, vous pouvez être explicite à propos de la mise en mémoire tampon des entrées en spécifiant différentes tailles pour l'un ou l'autre et non en spécifiant bs= (parce que c'est prioritaire) .

Par exemple, si vous faites :

IN| dd ibs=1| OUT

…puis un POSIX dd va write() en morceaux de 512 octets en collectant chaque read() individuellement octet dans un seul bloc de sortie.

Sinon, si vous le faites…

IN| dd obs=1kx1k| OUT

…un POSIX dd va read() au maximum 512 octets à la fois, mais write() chaque bloc de sortie de la taille d'un mégaoctet (le noyau permettant et exceptant éventuellement le dernier - parce que c'est EOF) intégralement en collectant les entrées dans des blocs de sortie de taille normale .

Également à partir de la spécification, cependant :

  • count=n
    • Copier uniquement n blocs d'entrée.

count= correspond à i?bs= blocs, et donc afin de gérer une limite arbitraire sur count= de manière portable, vous aurez besoin de deux dd s. La façon la plus pratique de le faire avec deux dd s est en canalisant la sortie de l'un vers l'entrée de l'autre, ce qui nous place sûrement dans le domaine de la lecture/écriture d'un fichier spécial quel que soit le type d'entrée d'origine.

Un tube IPC signifie que lors de la spécification de [io]bs= fait valoir que, pour le faire en toute sécurité, vous devez conserver ces valeurs dans le PIPE_BUF défini par le système limite. POSIX stipule que le noyau du système doit uniquement garantir la read() atomique s et write() s dans la limite de PIPE_BUF comme défini dans limits.h . POSIX garantit que PIPE_BUF être au moins

  • {_POSIX_PIPE_BUF}
    • Nombre maximal d'octets dont le caractère atomique est garanti lors de l'écriture dans un tube.
    • Valeur :512

(qui se trouve être aussi le dd par défaut taille de bloc d'e/s) , mais la valeur réelle est généralement d'au moins 4k. Sur un système Linux à jour, il s'agit par défaut de 64k.

Ainsi, lorsque vous configurez votre dd processus, vous devriez le faire sur un bloc facteur basé sur trois valeurs :

  1. bs =( obs =PIPE_BUF ou moins )
  2. n =nombre total souhaité d'octets lus
  3. compte =n / bs

Comme :

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

Vous devez synchroniser les entrées/sorties avec dd pour gérer les entrées non recherchables. En d'autres termes, rendez les pipe-buffers explicites et ils cessent d'être un problème. C'est ce que dd est pour. La quantité inconnue ici est yes 's buffer size - mais si vous bloquez cela à un connu quantité avec un autre dd alors une petite multiplication informée peut faire dd utilisation sûre pour copier des données (sans risque de corruption due à une lecture ou une écriture partielle) même en limitant arbitrairement l'entrée avec count= avec n'importe quel type d'entrée arbitraire sur n'importe quel système POSIX et sans manquer un seul octet.

Voici un extrait de la spécification POSIX :

  • ibs= expr
    • Spécifiez la taille du bloc d'entrée, en octets, par expr (la valeur par défaut est 512) .
  • obs= expr
    • Spécifiez la taille du bloc de sortie, en octets, par expr (la valeur par défaut est 512) .
  • bs= expr
    • Définir les tailles de bloc d'entrée et de sortie sur expr octets, remplaçant ibs= et obs= . Si aucune conversion autre que sync , noerror , et notrunc est spécifié, chaque bloc d'entrée doit être copié dans la sortie en tant que bloc unique sans agrégation de blocs courts.
En relation :Comment faire en sorte que le terminal affiche [email protected] en caractères gras ?

Vous trouverez également une partie de cela mieux expliquée ici.


Linux
  1. Lire et écrire des données de n'importe où avec la redirection dans le terminal Linux

  2. Utilisez la commande Netcat pour lire et écrire des données sur le réseau sur Ubuntu 20.04

  3. Dans Bash, quand créer un alias, quand créer un script et quand écrire une fonction ?

  4. Modifier l'autorisation de lecture, d'écriture et d'exécution ?

  5. Quand vérifier EINTR et répéter l'appel de fonction ?

Comment utiliser la commande Netcat pour lire et écrire des données sur le réseau

Trucs et astuces pour les administrateurs système et les opérations

Comment installer et utiliser Okteta pour les fichiers de données RAW sous Linux

Annonces textuelles au paiement par clic pour les avocats et les avocats

Qu'est-ce qu'une base de données distribuée et à quoi servent les systèmes de données distribués ?

Les 15 meilleurs logiciels économétriques et statistiques pour système Linux