Comment écraser le début d'un périphérique avec des bits randoms ou des zéros?

Il suffit de chercher à écraser une petite partie d'un périphérique avec des zéros ou des bits randoms. Plus précisément, je voudrais écraser le premier 1% de tous les secteurs ou quelques MiB. Y a-t-il un moyen facile de le faire?

Bien que /dev/urandom soit extrêmement lent et ne permette pas d'écraser de grandes quantités de données (disque entier), cela peut être utile pour les petites régions.

Exemple d'écrasement 8MiB:

 dd bs=1M count=8 iflag=fullblock if=/dev/urandom of=/dev/destroyme 

Vous pouvez également utiliser shred :

 shred -v -n 1 -s 8M /dev/destroyme 

Vous pouvez également utiliser losetup pour créer des périphériques de taille et de décalage spécifiques et les écraser avec des utilitaires qui n'ont pas leurs propres options de taille / décalage.

 losetup --find --show --offset 0 --sizelimit $((8*1024*1024)) /dev/destroyme # will print /dev/loopX cat /dev/urandom > /dev/loopX losetup -d /dev/loopX 

Identique à n'importe quel file pour écraser les 10 premiers MiB avec des zéros:

 head -c10M < /dev/zero 1<> /dev/sdax 

Pour les files de périphérique de bloc, le 1<> pour ouvrir sans troncature n'est même pas nécessaire car il n'y a pas de troncature de bloc, vous pouvez donc simplement faire:

 head -c10M < /dev/zero > /dev/sdax 

Toutes les implémentations de head ne prennent pas en charge -c , et quand elles le font, toutes ne supportent pas ce suffixe M (et quand elles le font, M peut signifier mégaoctet (1000000 octets) comme la head ksh93 embeddede ou mebibyte (1048576 octets). Dans ce cas, vous pouvez faire:

 head -c "$((10 * 1024 * 1024)" 

pour le rendre explicite.

Si nous comparons avec dd bs=1M count=10 < /dev/zero > /dev/sdax :

  • conceptuellement

    • head est la command pour lire un nombre spécifié d'octets ou de lignes à partir d'un file et l'écrire dans stdout
    • tandis que dd est une command de bas niveau pour lire et écrire des données exactement comme vous le souhaitez.

    J'utiliserais la head ici sur le terrain que c'est la command conçue pour la tâche. Je voudrais utiliser dd si je voulais optimiser pour un cas d'utilisation spécifique, ou utiliser l'une des fonctionnalités spécifiques de dd (voir aussi la note sur la portabilité).

  • La boucle de lecture + écriture:

    • head -c10M essaiera de lire datatables demandées, peu importe quoi et échouera seulement avec un état de sortie non nul si une erreur est rencontrée.
    • dd bs=1M count=10 , fait exactement 10 lectures (tant qu'il n'y a pas d'erreur) et pour chaque lecture qui renvoie certaines données, faites une écriture correspondante avec la quantité de données lues. Cela ne fonctionne que tant que chaque lecture returnne exactement le 1M demandé. En pratique, c'est vrai pour /dev/zero , mais sur Linux (4.6 au less), pour /dev/urandom , je ne peux pas get plus de 32MiB less 1 octet en une seule lecture (donc OK pour 1MiB, mais YMMV si vous utilisez une version différente de Linux), et pour /dev/random , seulement quelques octets (ce qui est actuellement dans le pool d'entropie). L'implémentation GNU de dd a un iflag=fullblock pour continuer à lire jusqu'à ce que le tampon de request soit plein pour se comporter comme la head , mais si vous n'avez pas GNU dd , la seule option est de faire 1 octet lit à un moment qui aurait dramatique impacts sur la performance.
  • performance: Pour toute autre chose que de petites quantités (less de quelques centaines de mégaoctets) où datatables sont écrites dans des memorys tampons qui seront écrasées sur le disque ultérieurement, ou écrites dans /dev/null , le process sera lié aux E / S. Si vous lisez dans /dev/urandom ou /dev/random le goulot de la bouteille sera la génération de nombres randoms ou les E / S de disque. Dans ces cas, vous ne findez pas beaucoup de différence entre dd et tête. Dans tous les cas, la head est susceptible d'avoir un overhead processeur plus élevé (inaperçu lorsque la performance est liée aux E / S).

    • head est un outil de base. Les implémentations vont essayer de faire le travail aussi efficacement que possible tout en restant fiables pour tous les types d'inputs et de sorties et ne pas utiliser trop de ressources. Il fera la même boucle de lecture + écriture que dd , la différence principale en termes de performances est la taille des lectures et des écritures qui déterminera le nombre d'appels système en cours.

      Ces tailles dépendront de l'implémentation et de la version de la head et éventuellement du système. Avec la dernière version de la head GNU sur mon système, les lectures sont de taille BUFSIZE (8KiB sur les systèmes GNU), et les écritures de taille 4KiB, bien que cela puisse être changé avec stdbuf -o 1M par exemple.

      La head ksh93 embeddede semble faire 64KiB lit et écrit et n'utilise pas la stdio de la libc, du less sur mon système.

      La head GNU utilisant stdio signifie également des frais généraux supplémentaires encourus par stdio (dont la mise en œuvre dépend du système).

      Les versions actuelles de GNU cat utilisent fadvise pour indiquer au système qu'il lira datatables de façon séquentielle afin d'optimiser la caching en conséquence. Il n'est pas impossible que certaines implémentations de la head fassent ou le feront à l'avenir. dd étant de bas niveau, je m'attendrais à ce qu'il le fasse seulement si vous le lui aviez dit (je ne suis au courant d'aucune implémentation de dd qui ait un tel support).

    • dd est un niveau très bas, il appellera directement les appels système read() et write() . Vous ne pouvez pas get beaucoup plus efficace que cela en utilisant des API spécialisées comme sendfile() Linux.

      Cela vous donne beaucoup plus de contrôle sur la taille de read() et write() , vous permettant ainsi de vous optimiser en fonction du type d'input / sortie et des ressources disponibles. Par exemple, si vous avez beaucoup de memory disponible, vous pouvez aussi bien lire datatables en une fois (même si dans mes tests lors de la copy de / dev / zero vers / dev / null, je ne vois aucune amélioration significative passé une taille de bloc de 32KiB et la performance commencent même à se dégrader après une taille de blocs de 1MiB).

  • portabilité

    Aucun de -c , bs=10M , conv=fullblock sont portables. La seule command POSIX permettant de lire une certaine quantité de données à partir d'un file est dd , mais pour l'utiliser de manière fiable (sauf sur /dev/zero ), comme indiqué ci-dessus, vous avez besoin de bs=1 .

  • conséquence d'erreurs d'écriture.

    Les deux head et dd quitteront immédiatement en essayant d'écrire après la fin du périphérique de bloc. Si le disque a des secteurs défaillants, cela ne sera généralement pas détecté car l'écriture sur le disque est asynchronous. Avec l'implémentation GNU de dd , vous pouvez forcer l'écriture à être directe avec oflag=direct ce qui signifie que dd s'arrêtera sur la première erreur. Vous pouvez utiliser la taille de bloc par défaut de 512, puis si vous voulez écrire autant que possible avant le premier secteur défaillant.

  • conséquence d'erreurs de lecture

    Vous ne devriez pas get d'erreur de lecture sur / dev / zero, / dev / urandom ou / dev / random. Mais plus généralement, la head et le dd sortiront avec une erreur sur la première erreur de lecture. Avec dd , vous pouvez continuer sur les erreurs avec conv=noerror . Dans ce cas, vous voudrez probablement append l'option de sync ( conv=noerror,sync ) afin que les blocs défaillants soient remplis de zéros. head ne vous donnera pas la possibilité de le faire car il n'a pas été conçu pour cela.

Alternatives.

  • pv -Ss 10M < /dev/zero > /dev/sdax va copyr ces 10M et vous donner une barre de progression. Par défaut, la taille de lecture / écriture est 128KiB dans mon test. Vous pouvez le changer avec l'option -B , mais dans mes tests, 128KiB donne déjà les meilleurs résultats. pv a une option -E équivalente à dd conv=noerror,sync .

    Sous Linux, c'est aussi bon pour les E / S sur les canaux car il utilise l'appel système splice() pour optimiser les performances.

  • Si vous voulez jouer avec l'appel système sendfile() , vous pouvez utiliser xfs_io .

     xfs_io -c 'sendfile -i src 0 10M' dst 

    envoie 10M de src à dst. Cependant, il ne fait qu'un sendfile() et l'appel système ne peut pas être utilisé sur /dev/zero , /dev/random ni /dev/urandom . Il peut être utilisé sur des files clairsemés.

      truncate -s 1T empty-file xfs_io -c 'sendfile -i empty-file 0 10M' /dev/sdax 

    cela fonctionnerait mais pour de grandes quantités (plusieurs Gibibytes), parce que c'est un appel système sendfile() , beaucoup de memory doit être alloué ce qui signifie que ça va être less efficace que dd bs=1M . Idéalement, nous voudrions faire plusieurs sendfile() s de seulement quelques mégabits à la fois, mais je ne suis pas au courant de la command qui le fait.

  • Pour l'input /dev/zero , vous n'avez pas vraiment besoin de lire datatables pour chaque bloc que vous écrivez. Après tout, ce n'est que des zéros. Il est assez facile de créer un tampon avec seulement des zéros sans avoir à lire /dev/zero , et nous pouvons le réutiliser entre chaque écriture. Par exemple:

     PERLIO=:unix perl -e '$x = pack("x" . 1024*1024); print $x for 1..10000' > /dev/sdax 

écrire 10000 MiB serait beaucoup plus efficace (malgré les frais généraux perl ) que toute solution qui lit plusieurs fois /dev/zero .

Vous pouvez utiliser dd pour faire cela en utilisant le périphérique /dev/urandom qui vous fournira des données randoms. Un exemple :

 dd if=/dev/urandom of=/dev/sdX bs=1M count=100 

cela écrira 100mbyte de données randoms:

  • if est le file d'input
  • of est le file de sortie
  • bs=1M est la taille du bloc 1Moctet
  • count est combien de fois devrait être ces blocs écrits

Vous pouvez aussi bien utiliser comme file d'input /dev/zero , /dev/null ou n'importe quoi d'autre qui vous fournit des données. Cette command commencera à écrire datatables au début du file / périphérique de sortie.