Pourquoi dd de / dev / random donne-t-il différentes tailles de fichier?

26

J'exécute la commande suivante sur un système Ubuntu:

dd if=/dev/random of=rand bs=1K count=2

Cependant, chaque fois que je l'exécute, je me retrouve avec un fichier d'une taille différente. Pourquoi est-ce? Comment puis-je générer un fichier d'une taille donnée rempli de données aléatoires?

Daniel
la source
1
/dev/randomse bloquera s'il n'y a pas assez d'entropie disponible pour générer le nombre de chiffres souhaité. il faut simplement du temps pour rassembler cette quantité de "randomité" aléatoire de haute qualité ... Soit utiliser /dev/urandompour une valeur "aléatoire" moins aléatoire, soit vérifier votre pool d'entropie (en boucle, et attendre au besoin) ...
Peter.O
Voir aussi cette question .
Keith Thompson
3
il suffit d'ajouteriflag=fullblock
frostschutz

Réponses:

31

Vous observez une combinaison du comportement particulier de ddavec le comportement particulier de Linux /dev/random. Soit dit en passant, les deux sont rarement le bon outil pour le travail.

Linux /dev/randomretourne les données avec parcimonie. Il est basé sur l'hypothèse que l'entropie dans le générateur de nombres pseudo-aléatoires s'éteint à un rythme très rapide. Comme la collecte d'une nouvelle entropie est lente, elle /dev/randomn'abandonne généralement que quelques octets à la fois.

ddest un ancien programme grincheux initialement destiné à fonctionner sur des périphériques à bande. Lorsque vous lui dites de lire un bloc de 1 Ko, il tente de lire un bloc. Si la lecture renvoie moins de 1024 octets, difficile, c'est tout ce que vous obtenez. Fait donc dd if=/dev/random bs=1K count=2deux read(2)appels. Puisqu'il lit /dev/random, les deux readappels ne retournent généralement que quelques octets, en nombre variable selon l'entropie disponible. Voir aussi Quand dd convient-il pour copier des données? (ou, quand sont read () et write () partial)

À moins que vous ne conceviez un programme d'installation ou un cloneur de système d'exploitation, vous ne devriez jamais utiliser /dev/randomsous Linux, toujours /dev/urandom. La urandompage de manuel est quelque peu trompeuse; /dev/urandomest en fait adapté à la cryptographie, même pour générer des clés à longue durée de vie. La seule restriction /dev/urandomest qu'il doit être alimenté avec une entropie suffisante; Les distributions Linux enregistrent normalement l'entropie entre les redémarrages, donc la seule fois où vous pourriez ne pas avoir suffisamment d'entropie est sur une nouvelle installation. L'entropie ne s'use pas en termes pratiques. Pour plus d'informations, lisez Un rand de / dev / urandom est-il sécurisé pour une clé de connexion? et alimentation / dev / pool d'entropie aléatoire? .

La plupart des utilisations de ddsont mieux exprimées avec des outils tels que headou tail. Si vous voulez 2 Ko d'octets aléatoires, exécutez

head -c 2k </dev/urandom >rand

Avec les noyaux Linux plus anciens, vous pourriez vous en sortir avec

dd if=/dev/urandom of=rand bs=1k count=2

car /dev/urandomheureusement retourné autant d'octets que demandé. Mais cela ne l' est plus depuis le noyau 3.16, il est maintenant limité à 32Mo .

En général, lorsque vous devez utiliser ddpour extraire un nombre fixe d'octets et son entrée ne provient pas d'un fichier régulier ou un périphérique de bloc, vous devez lire octet par octet: dd bs=1 count=2048.

Gilles 'SO- arrête d'être méchant'
la source
Merci pour l'astuce sur l'utilisation de la tête au lieu de dd. Cela me permet d'utiliser toujours / dev / random si je le souhaite. Bien que / dev / urandom soit probablement suffisant comme vous le mentionnez, il est agréable de savoir comment utiliser / dev / random si le besoin s'en fait sentir.
Daniel
sur les noyaux depuis 3,16 /dev/urandom renvoie 32m perread() .
mikeserv
Alternativement, si vous avez besoin d'une commande compatible POSIX, vous pouvez utiliser l'astuce ici: unix.stackexchange.com/a/192114 dd if=/dev/urandom ibs=1k obs=1k | dd bs=1k count=2
Rufflewind
11

Depuis man 4 randomune box RHEL 5:

Lors de la lecture, le périphérique / dev / random ne renverra que des octets aléatoires dans le nombre estimé de bits de bruit dans le pool d'entropie.

J'obtiens des fichiers de taille 213 octets sur cette machine. Retour à l'homme 4 au hasard:

Lorsqu'il est lu, le périphérique / dev / urandom renvoie autant d'octets que nécessaire.

J'obtiens 2048 octets de chaque invocation de dd if=/dev/urandom of=rand bs=1K count=2

Je conclus que la différence est due à la quantité d'entropie générée par votre machine entre les invocations de dd if=/dev/random ...

Bruce Ediger
la source
Ouais, pratiquement, à moins qu'il ne soit dans une vraie application de cryptographie, @Daniel devrait utiliser / dev / urandom. Mais je suis perplexe quant à pourquoi dd if=/dev/random bs=1K count=2s'arrête lorsque la piscine d'entropie est apparemment vidé. À partir des documents, il devrait se bloquer jusqu'à ce qu'il y ait plus d'entropie, de sorte ddque le fichier sera écrit lentement, au lieu de simplement vider le pool actuel et de quitter.
cjc
Je me posais également des questions à ce sujet, mais il est cohérent dans RHEL, Slackware 13.1 et un Arch assez récent. Le RHEL était x86_64, les autres étaient 32 bits. Malheureusement, les dd docs sont au format GNU info, donc je ne les ai pas tous lus.
Bruce Ediger
Il est également cohérent sur Gentoo.
Matthew Scharley
4
@cjc: C'est parce que lorsque vous appelez read(fd, mybuf, 1024)un FD bloquant, il revient dès que l'appareil sous-jacent renvoie des données. S'il y a 1024 octets à lire, il retourne cela. S'il n'y a que 201 octets, il retournera 201. S'il n'y a aucun octet disponible, il se bloquera jusqu'à ce qu'au moins un octet devienne disponible, puis le / les retournera.
Warren Young
@WarrenYoung la lecture de / dev / random vide-t-elle son contenu? Je suppose que oui.
Michael Martinez
5

Pourquoi ddsupprime- t-il des données? ... Gilles a posé cette question intéressante à propos de dd:
Quand dd convient-il pour copier des données? (ou, quand sont read () et write () partial)
Voici un extrait de cette question:

    * ... ce 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 fichier de sortie (il est probable qu'il soit bien en dessous de 10 Mo).


Mis à part mon commentaire (à la fin de votre question), quelque chose comme ça est intéressant à regarder ... Il attrape vos octets dans le fichier $trnd. J'ai choisi semi-arbitrairement bs = 8

Déplacez votre souris et regardez-la accélérer.
Avec mon ordinateur inactif (AFK et aucune activité réseau), et après avoir épuisé le pool d'entropie, il a fallu 2 heures 12 minutes pour collecter seulement 1192 octets, moment auquel je l'ai annulé.

Puis, me déplaçant la souris continue, il a fallu relativement beaucoup moins 1 minute 15 secondes pour recueillir le même nombre d'octets.

Cela montre assez clairement que la collecte de l' entropie n'est pas la vitesse du processeur en fonction, mais il est des événements aléatoires sur la base, et que mon système Ubuntu utilise la souris comme l' un de ses importants facteurs aléatoires.

get=2048
trnd=/tmp/$USER.rnd; >"$trnd"
while (( $(wc -c <"$trnd") < $get )) ;do
    dd if=/dev/random bs=8 count=1 2>/dev/null >>"$trnd"
    echo -n "itt: $((i+=1))  ct: "; wc -c <"$trnd"
done
truncate -s $get "$trnd"
echo -e "\nfinal count: "; wc -c <"$trnd"
Peter.O
la source
1

ddest conçu pour le blocage - c'est généralement le meilleur outil à votre disposition pour lire à partir d'entrées de taille variable si vous en avez besoin immédiatement car ddil ne tamponnera pas les lectures en cours dans un futur futur write() (sauf si vous le configurez de manière très explicite de cette façon avec des obs plus grandes que ibs) , mais va à la place write()tout ce qu'il lit dès qu'il read()est (et éventuellement le traite) .

Voici quelques définitions importantes :

  • ibs=expr
    • Spécifiez la taille du bloc d'entrée, en octets, par (la valeur par défaut est 512) .expr
  • obs=expr
    • Spécifiez la taille du bloc de sortie, en octets, par (la valeur par défaut est 512) .expr
  • bs=expr
    • Définissez les tailles des blocs d'entrée et de sortie sur exproctets, remplaçant ibs=et obs=. Si aucune conversion autre que sync, noerroret notruncn'est spécifiée, chaque bloc d'entrée doit être copié dans la sortie en tant que bloc unique sans agréger les blocs courts.

Donc, vous voyez, quand ibset obssont définis ensemble, bsalors ibsprioritaire - mais sinon, si vous êtes spécifique, alors soit obsou le cbsfait.

Voici un exemple dans lequel ibsest le plus important. Vous pourriez faire quelque chose comme ça si vous vouliez suivre la vitesse à laquelle la /dev/randompiscine s'est remplie ...

dd "ibs=$size" conv=sync "count=$lmt" \ 
    if=/dev/random of="$somefile"

Tant que if=la cible de 'est lisible, cela se traduira toujours par le même fichier de sortie de taille, car ddles syncblocs seront lus sur les valeurs nulles. En d'autres termes, si dd read()s pour un bloc d'entrée de $((size=10)) $((count=5))temps et que le read()fichier renvoie 2 octets, puis 8 octets, puis 12 octets, puis 2 octets, puis 4 octets, ddécrira dans son fichier de sortie quelque chose comme

 2 read bytes 8NULs \
 8 read bytes 2NULs \
10 read bytes 0NULs \
 4 read bytes 6NULs \
 4 read bytes 6NULs

... car dd, par défaut, ne tarde pas . Donc, si vous avez besoin de suivre en continu et de délimiter les écritures d'un autre processus, ddl'outil est pour vous.

Si vous écrivez simplement une certaine quantité de données dans un fichier normal, contrairement à d'autres déclarations faites ici, vous pouvez également l'utiliser ddpour cela - et assez facilement - mais vous en aurez besoin de plusieurs et d'un facteur de blocage fiable .

Par exemple, si vous l'avez fait:

{   dd ibs="$size" obs="${size}x$block_factor" |
    dd bs="${size}x$blockfactor" "count=$lmt"
}  <infile >outfile

... le premier ddtamponnerait autant de ibs="$size"blocs d'entrée que nécessaire pour remplir au moins un obs="${size}x$block_factor"bloc de sortie pour chacun write()du tuyau entre celui-ci et le second dd. Cela signifie que le second ddpeut limiter la sortie de manière fiable avec count="$lmt"car tous les write()s que le premier fait correspondront à sa taille de bloc d'E / S - quel que soit le nombre de read()s que le premier dddoit faire pour le faire.

Et c'est ainsi que vous pouvez utiliser ddpour lire de manière fiable des tuyaux ou d'autres types de fichiers spéciaux - avec juste un peu de calcul.

mikeserv
la source