Il y a un fichier, sur la machine où vous lisez ceci, qui est ouvert par plus de programmes que n’importe quel autre. Il ne contient rien. Il ne contiendra jamais rien. C’est précisément son métier.
/dev/null est le seau dans lequel on jette ce qu’on ne veut pas voir. On écrit
commande > /dev/null et le bavardage disparaît. Tout le monde connaît le geste. J’ai voulu
regarder l’objet.
Ce n’est pas un fichier vide
Première surprise, et elle tient dans une lettre.
$ stat -f '%HT %Sp %z' /dev/null
Character Device crw-rw-rw- 0
Un fichier vide ordinaire rendrait Regular File -rw-r--r-- 0. Les deux ont une taille de zéro,
et ce zéro ne veut pas du tout dire la même chose. Pour le fichier ordinaire, c’est la taille de
ce qu’il contient. Pour /dev/null, il n’y a pas de contenu dont on puisse prendre la taille :
ce n’est pas un récipient, c’est une porte.
Le c en tête de crw-rw-rw- le dit : périphérique caractère. Derrière, pas un espace sur le
disque, mais un morceau de code du noyau, désigné par deux numéros, majeur 3 et mineur 2 sur
cette machine. Le majeur nomme le pilote, le mineur la sortie qu’on emprunte. /dev/zero, son
voisin, est le 3 et le 3.
Le reste des droits mérite un regard aussi. rw-rw-rw- : tout le monde peut écrire. Sur
n’importe quel autre fichier du système, ce serait une faute grave. Ici c’est la condition pour
que ça serve à quelque chose. Et ce n’est pas une exception isolée : sur les 321 périphériques
caractère de ce /dev, 278 sont ouverts en écriture à tout le monde.
Il avale six fois plus vite qu’un vrai fichier
Cent mégaoctets, dans les deux cas, sur la même machine :
| Destination | Temps | Débit |
|---|---|---|
/dev/null | 0,002239 s | 46,8 Go/s |
| un vrai fichier | 0,014056 s | 7,46 Go/s |
Six fois plus vite, et encore, le vrai fichier bénéficie du cache d’écriture. Le chiffre de 46 gigaoctets par seconde n’est pas une performance de disque : c’est le coût de traverser le noyau et de ne rien faire. Il n’y a rien à optimiser plus bas que ne rien faire.
En lecture, /dev/null répond autre chose, et aussitôt : zéro octet, fin de fichier. C’est donc
un puits sans fond dans un sens et un fichier vide dans l’autre, ce qui explique le deuxième
usage de la chose : cp /dev/null journal.log vide un fichier journal sans le supprimer. Sur un
fichier de 21 octets, essayé ici : 21 avant, 0 après, le même inode, les programmes qui l’ont
ouvert ne s’aperçoivent de rien.
Ce qui arrive quand ce n’est plus un périphérique
C’est là que l’histoire devient concrète, parce que c’est une panne d’administration classique.
Supprimez /dev/null et laissez un programme écrire dedans : le système crée un fichier
ordinaire du même nom. Rien ne proteste, rien ne prévient.
J’ai fabriqué un faux null, un fichier régulier, et je lui ai envoyé les mêmes cent
mégaoctets :
faux null : Regular File, 104857600 octets
vrai /dev/null : Character Device, 0 octet
Le faux a grossi de cent mégaoctets. Le vrai n’a pas bougé d’un octet. Multipliez par un serveur
qui écrit ses journaux dans /dev/null toute la nuit, et vous avez un disque plein au matin,
avec un fichier de plusieurs gigaoctets nommé null qui n’aurait jamais dû exister.
La trouvaille : il tient un journal
Je ne m’y attendais pas, et c’est ce qui m’a fait écrire ce texte.
/dev/null ne garde rien de ce qu’on lui donne. Mais il note l’heure à laquelle on le lui a
donné. Trois écritures d’un seul octet, une par seconde :
ecriture 1 -> 10:44:20
ecriture 2 -> 10:44:21
ecriture 3 -> 10:44:22
Sa date de modification avance à chaque fois. Sa date d’accès avance à chaque lecture. Le trou noir tient un registre de ses visites, sans rien garder de ce qu’on y a versé.
Il y a une logique à ça, et elle dit quelque chose du système : ces dates ne décrivent pas le
contenu, elles décrivent l’usage de la porte. Le noyau tient la même comptabilité pour un
terminal, une imprimante ou un disque. /dev/null n’a droit à aucun traitement de faveur, et
c’est bien ainsi qu’il faut le comprendre : ce n’est pas un cas particulier du système de
fichiers, c’est le système de fichiers appliqué à quelque chose qui n’est pas un fichier.
Sa date de création, pendant qu’on y est : 1er janvier 1970. L’heure zéro d’Unix. Elle n’a pas été choisie pour faire joli, c’est simplement la valeur par défaut d’un objet qui ne naît pas d’une écriture sur un disque.
1 710 ouvertures, à l’instant
J’ai compté qui le tient ouvert, à une seconde donnée, sur cette machine ordinaire :
1 710 ouvertures, par 361 programmes différents. Les services du système en tête, le navigateur, l’indexeur de fichiers, les processus Node. Aucun ne s’en sert pour stocker quoi que ce soit. Tous s’en servent pour avoir un endroit où écrire, parce qu’un programme qui ne peut pas écrire sur sa sortie d’erreur se comporte souvent plus mal qu’un programme qui écrit dans le vide.
C’est le point que je retiens. On présente toujours /dev/null comme une poubelle. Ce n’est pas
une poubelle, c’est une destination valide, et sa valeur ne tient pas à ce qu’elle garde,
mais à ce qu’elle garantit : une écriture qui réussit toujours, immédiatement, sans limite de
place. Dans un système où tout est fichier, il fallait bien un fichier qui prouve que l’interface
compte plus que le contenu.
Ce que je n’ai pas mesuré
Les numéros majeur et mineur relevés ici, 3 et 2, sont ceux de macOS : je n’ai pas vérifié
ailleurs, et Linux n’utilise pas les mêmes. Je n’ai pas testé le comportement sous BSD, ni ce
que fait un système de fichiers en lecture seule quand on y recrée un faux null. Et je n’ai
pas regardé si les dates avancent aussi lorsque l’écriture vient d’un autre utilisateur.
Les chiffres d’ouverture et de débit sont ceux d’une machine, un jour, à une heure. Pris deux
minutes plus tard, ils seront différents. Ce qui ne changera pas, c’est la lettre c.