Pourquoi / dev / null est-il un fichier? Pourquoi sa fonction n'est-elle pas implémentée comme un simple programme?

114

J'essaie de comprendre le concept de fichiers spéciaux sous Linux. Toutefois, /devà ma connaissance , avoir un fichier spécial dans le fichier semble tout à fait ridicule, alors que sa fonction pourrait être mise en œuvre par une poignée de lignes en C.

De plus, vous pouvez l'utiliser à peu près de la même manière, c.-à- nulld /dev/null. Y a-t-il une raison spécifique de l'avoir en fichier? Faire un fichier ne cause pas beaucoup de problèmes comme trop de programmes accédant au même fichier?

Ankur S
la source
20
Incidemment, une grande partie de ces frais généraux est également pourquoi cat foo | barest bien pire (à l'échelle) que bar <foo. catest un programme trivial, mais même un programme trivial crée des coûts (certains d’entre eux sont spécifiques à la sémantique FIFO - car les programmes ne peuvent pas seek()entrer dans les FIFO, par exemple, un programme qui pourrait être mis en œuvre efficacement avec la recherche peut aboutir à des opérations beaucoup plus coûteuses lorsqu’un pipeline est créé, avec un périphérique de type caractère, /dev/nullil peut simuler ces opérations, ou avec un fichier réel, il peut les implémenter, mais une FIFO ne permet aucun type de traitement contextuel).
Charles Duffy
13
grep blablubb file.txt 2>/dev/null && dosomethingne pourrait pas fonctionner avec null étant un programme ou une fonction.
rexkogitans
16
Vous trouverez peut-être éclairant (ou du moins épanouissant l'esprit) de lire sur le système d'exploitation Plan 9 pour voir où se dirigeait la vision "tout est un fichier" - il devient un peu plus facile de voir le pouvoir de disposer de ressources disponibles sous forme de fichier les chemins une fois que vous voyez un système embrassant pleinement le concept (plutôt que la plupart / partiellement, comme le font les Linux / Unix modernes).
mtraceur
25
De plus, personne n’indiquant qu’un pilote de périphérique fonctionnant dans l’espace noyau est un programme comportant "quelques lignes de C" , aucune des réponses jusqu’à présent n’a en fait traité la supposition de "trop ​​de programmes accédant au même fichier". dans la question.
JdeBP
12
Re "sa fonction pourrait être implémentée par une poignée de lignes en C": Vous n’y croiriez pas, mais elle est implémentée par une poignée de lignes en C! Par exemple, le corps de la readfonction pour /dev/nullest composé d'un "retour 0" (ce qui signifie qu'il ne fait rien et que, je suppose, il en résulte un EOF): (De static github.com/torvalds/linux/blob/master/ drivers / char / mem.c ) ssize_t read_null(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; }(Oh, je viens de voir que @JdeBP a déjà souligné ce point. En tout cas, voici l’illustration :-).
Peter A. Schneider

Réponses:

153

Outre les avantages en termes de performances liés à l'utilisation d'un périphérique spécial caractères, le principal avantage est la modularité . / dev / null peut être utilisé dans presque tous les contextes dans lesquels un fichier est attendu, pas seulement dans les pipelines de shell. Considérez les programmes qui acceptent les fichiers en tant que paramètres de ligne de commande.

# We don't care about log output.
$ frobify --log-file=/dev/null

# We are not interested in the compiled binary, just seeing if there are errors.
$ gcc foo.c -o /dev/null  || echo "foo.c does not compile!".

# Easy way to force an empty list of exceptions.
$ start_firewall --exception_list=/dev/null

Ce sont tous des cas où l'utilisation d'un programme en tant que source ou puits serait extrêmement fastidieuse. Même dans le cas du pipeline shell, stdout et stderr peuvent être redirigés indépendamment vers des fichiers, ce qui est difficile à faire avec des exécutables en tant que récepteurs:

# Suppress errors, but print output.
$ grep foo * 2>/dev/null
ioctl
la source
14
En outre, vous n'utilisez pas /dev/nulluniquement les commandes du shell. Vous pouvez l'utiliser dans d'autres paramètres fournis à un logiciel, par exemple dans des fichiers de configuration. --- Fort le logiciel c'est très pratique. Il n'est pas nécessaire de faire la différence entre /dev/nullun fichier et un fichier normal.
Pabouk
Je ne suis pas sûr de comprendre la partie sur les redirections séparées pour réduire les exécutables étant difficile. En C, vous faites juste un pipe, forket execvecomme toute autre tuyauterie de traitement, seulement avec des modifications aux dup2appels qui établissent les connexions, non? Il est vrai la plupart des shells n'offrent pas les moyens de le faire plus jolies, mais sans doute si nous ne disposions pas tant que périphérique fichier modèle et la plupart des choses /devet /procont été traités comme des executables, des obus aurait été conçu avec des moyens pour le faire aussi facilement que nous redirigeons maintenant.
Aschepler
6
@aschepler C'est qu'il est difficile de ne pas rediriger des exécutables creux. C'est que l'écriture d'applications capables d'écrire / lire à partir des deux fichiers et du récepteur nul serait plus compliquée si le récepteur nul n'était pas un fichier. À moins que vous ne parliez d'un monde où au lieu de tout être un fichier, tout est un exécutable? Ce serait un modèle très différent de ce que vous avez dans * nix OS.
Cubique
1
@aschepler Vous avez oublié wait4! Vous avez raison, il est certainement possible de diriger stdout et stderr vers différents programmes utilisant des apis POSIX, et il serait peut-être possible d'inventer une syntaxe de shell intelligente pour rediriger stdout et stderr vers différentes commandes. Cependant, je ne suis au courant d'aucun shell de ce type à l'heure actuelle, et le point le plus important est que / dev / null s'inscrit parfaitement dans les outils existants (qui fonctionne généralement avec des fichiers), et que / bin / null ne le ferait pas. Nous pourrions également imaginer une API IO qui permette à gcc de transférer aussi facilement (en toute sécurité!) Vers un programme que vers un fichier, mais ce n'est pas la situation dans laquelle nous nous trouvons.
ioctl
2
@ioctl concernant les obus; zsh et bash au moins vous permettront de faire des choses comme grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile), le résultat est traité séparément errfileetoutfile
Matija Nalis
62

En toute justice, ce n'est pas un fichier régulier en soi; c'est un dispositif spécial de personnage :

$ file /dev/null
/dev/null: character special (3/2)

Son fonctionnement en tant que périphérique plutôt qu’en tant que fichier ou programme signifie qu’il est plus simple de rediriger ses entrées ou ses sorties, car il peut être attaché à n’importe quel descripteur de fichier, y compris les entrées / sorties / erreurs standard.

DopeGhoti
la source
24
cat file | nullaurait beaucoup de frais généraux, d' abord dans la mise en place d' un tuyau, fraie un processus, l' exécution de « nul » dans le nouveau processus, etc., aussi nulls'utiliseraient un peu de CPU dans un octets de lecture en boucle dans un tampon qui est plus tard juste mis au rebut ... La mise /dev/nullen oeuvre de dans le noyau est simplement plus efficace de cette façon. De plus, que se passe-t-il si vous souhaitez passer /dev/nullen argument, au lieu d'une redirection? (Vous pouvez utiliser <(...)bash, mais c'est beaucoup plus lourd!)
Filbranden
4
Si vous deviez diriger vers un programme nommé nullAu lieu d'utiliser la redirection vers /dev/null, y aurait-il un moyen simple et clair de dire au shell d'exécuter un programme tout en envoyant simplement son stderr to null?
Mark Plotnick
5
Il s’agit d’une installation très coûteuse pour une démonstration en tête à tête. Je suggère d'utiliser à la /dev/zeroplace.
Chrylis -on grève-
20
Ces exemples sont faux. dd of=-écrit dans un fichier appelé -, omettez simplement of=d’écrire sur stdout car c’est là que l’ ddécriture par défaut. Piping to falsene fonctionnerait pas car falseil ne lisait pas son stdin, il ddserait donc tué avec un SIGPIPE. Pour une commande qui supprime son entrée , vous pouvez utiliser ... cat > /dev/null. De plus, la comparaison qui ne serait probablement pas pertinente en tant que goulot d'étranglement serait probablement la génération de nombres aléatoires ici.
Stéphane Chazelas
8
Les versions AST de ddetc. ne font même pas la peine de faire un appel système en écriture quand ils détectent que la destination est / dev / null.
Mark Plotnick
54

Je soupçonne que le pourquoi a beaucoup à faire avec la vision / conception qui a façonné Unix (et par conséquent Linux), et les avantages qui en découlent.

Nul doute qu’il ya un avantage non négligeable sur le plan des performances à ne pas créer un processus supplémentaire, mais je pense qu’il ya plus à faire: Early Unix avait une métaphore "tout est un fichier", ce qui présente un avantage non évident mais élégant si vous le regardez. cela du point de vue du système plutôt que du point de vue des scripts shell.

Supposons que vous ayez votre nullprogramme de ligne de commande et /dev/nullle nœud de périphérique. Du point de vue des scripts shell, le foo | nullprogramme est réellement utile et pratique , et foo >/dev/nullprend un peu plus de temps à taper et peut sembler étrange.

Mais voici deux exercices:

  1. Nous allons mettre en œuvre le programme en nullutilisant des outils Unix existants et /dev/null- facile: cat >/dev/null. Terminé.

  2. Pouvez-vous mettre /dev/nullen œuvre en termes de null?

Vous avez absolument raison de dire que le code C destiné à supprimer une entrée est trivial, il est donc difficile de comprendre pourquoi il est utile de disposer d'un fichier virtuel pour la tâche.

Prenez en compte que presque tous les langages de programmation doivent déjà utiliser des fichiers, des descripteurs de fichiers et des chemins de fichiers, car ils faisaient partie du paradigme d'Unix "tout est un fichier" depuis le début.

Si tout ce que vous avez sont des programmes qui écrivent sur stdout, eh bien, le programme ne se soucie pas de les rediriger vers un fichier virtuel qui englobe toutes les écritures ou un canal vers un programme qui englobe toutes les écritures.

Maintenant, si vous avez des programmes qui utilisent des chemins de fichiers pour lire ou écrire des données (ce que font la plupart des programmes), et que vous souhaitez ajouter une fonctionnalité "entrée vide" ou "supprimer cette sortie" à ces programmes, /dev/nullcela est gratuit.

Notez que son élégance est de réduire la complexité du code de tous les programmes impliqués - pour chaque cas d'utilisation commun mais particulier que votre système peut fournir en tant que "fichier" avec un "nom de fichier" réel, votre code peut éviter d'ajouter une commande personnalisée. -ligne les options et les chemins de code personnalisés à gérer.

Une bonne ingénierie logicielle dépend souvent de la recherche de métaphores «naturelles» pour résumer certains éléments d’un problème d’une manière qui facilite la réflexion mais reste souple , de sorte que vous puissiez résoudre pratiquement le même éventail de problèmes de niveau supérieur sans avoir à consacrer temps et énergie mentale à réimplémenter en permanence des solutions aux mêmes problèmes de niveau inférieur.

« Tout est un fichier » semble être une telle métaphore pour l' accès aux ressources: Vous appelez opend'un chemin donné dans un espace de noms heirarchical, en obtenant une référence (descripteur de fichier) à l'objet, et vous pouvez readet write, etc sur les descripteurs de fichiers. Votre stdin / stdout / stderr sont également des descripteurs de fichier qui viennent d’être pré-ouverts pour vous. Vos canaux ne sont que des fichiers et des descripteurs de fichiers, et la redirection de fichiers vous permet de coller tous ces éléments ensemble.

Unix a tout autant réussi en partie à cause de la manière dont ces abstractions ont bien fonctionné et /dev/nullest mieux compris en tant que partie intégrante de cet ensemble.


Post-scriptum Il convient de regarder la version Unix de "Tout est un fichier", entre autres, /dev/nullcomme les premiers pas vers une généralisation plus souple et plus puissante de la métaphore mise en œuvre dans de nombreux systèmes qui ont suivi.

Par exemple, sous Unix, des objets spéciaux de type fichier, tels que ceux-ci, /dev/nulldevaient être implémentés dans le noyau lui-même, mais il s'avère assez utile d’exposer les fonctionnalités sous forme de fichier / dossier qui, depuis lors, ont permis de créer plusieurs systèmes qui permettent aux programmes pour faire ça.

L'un des premiers était le système d'exploitation Plan 9, créé par les mêmes personnes qui ont créé Unix. Plus tard, GNU Hurd a fait quelque chose de similaire avec ses "traducteurs". Pendant ce temps, Linux finissait par obtenir FUSE (qui s’est également propagé aux autres systèmes traditionnels).

métreur
la source
8
@PeterCordes le point de la réponse est de ne pas comprendre la conception. Si tout le monde comprenait déjà le design, cette question n'existerait pas.
OrangeDog
1
@mtraceur: Monter un fichier image sans autorisation root? montre certaines preuves que FUSE peut ne pas nécessiter de root, mais je ne suis pas sûr.
Peter Cordes
1
@PeterCordes RE: "semble bizarre": Ce n'est pas une excuse pour la conception, mais une reconnaissance de ce que cela peut sembler si vous ne songez pas à l'implémentation du système, et si vous n'avez pas encore eu le moment EUREKA à propos du système avantages de conception à l'échelle. J'ai essayé de le préciser en ouvrant cette phrase avec "du point de vue d'un script shell" et en faisant allusion au contraste entre cela et une perspective système quelques phrases avant. Après réflexion, "peut sembler bizarre" est préférable, je vais donc le peaufiner. J'apprécie les suggestions de libellé supplémentaires pour clarifier le texte sans le rendre trop verbeux.
mtraceur
2
La toute première chose qui m'a été racontée en tant que jeune ingénieur concernant Unix était "Tout est fichier" et je jure que vous pouvez entendre les majuscules. Et trouver cette idée très tôt rend Unix / Linux plus facile à comprendre. Linux a hérité de la majeure partie de cette philosophie de conception. Je suis content que quelqu'un en ait parlé.
StephenG
2
@PeterCordes, DOS "a résolu" le problème de frappe en faisant NULapparaître le nom de fichier magique dans chaque répertoire, c’est-à-dire que tout ce que vous devez taper est > NUL.
Cristian Ciupitu
15

Je pense /dev/nullest un périphérique de caractère (qui se comporte comme un fichier ordinaire) au lieu d'un programme pour des raisons de performances .

S'il s'agissait d'un programme, il faudrait charger, démarrer, planifier, exécuter, puis arrêter et décharger le programme. Le programme C simple que vous décrivez ne consomme évidemment pas beaucoup de ressources, mais je pense que cela fait une différence significative quand on considère un grand nombre (disons des millions) d’actions de redirection / canalisation, car les opérations de gestion de processus sont coûteuses à grande échelle, car ils impliquent des changements de contexte.

Autre hypothèse: la canalisation dans un programme nécessite que la mémoire alloue de la mémoire (même si elle est immédiatement supprimée). Donc, si vous dirigez dans l'outil, vous avez la double consommation de mémoire, une fois sur le programme d'envoi et à nouveau sur le programme de réception.

utilisateur5626466
la source
10
Ce n'est pas seulement le coût d'installation, c'est que chaque écriture dans un tube nécessite une copie en mémoire et un changement de contexte en programme de lecture. (Ou au moins un changement de contexte quand le tampon de canal est plein. Et le lecteur doit faire une autre copie quand il contient readles données). Ce n’est pas négligeable sur un PDP-11 monocœur où Unix a été conçu! La bande passante mémoire / copie est beaucoup moins chère aujourd'hui qu'elle ne l'était alors. Un writeappel système à une FD ouverte le /dev/nullpeut revenir immédiatement sans même lire les données de la mémoire tampon.
Peter Cordes
@PeterCordes, ma note est tangentielle, mais il est possible que, paradoxalement, la mémoire écrite aujourd'hui coûte plus cher que jamais. Un processeur à 8 cœurs exécute potentiellement 16 opérations sur un nombre entier d’heures dans une horloge, alors que l’écriture en mémoire de bout en bout prendrait fin par exemple en 16 horloges (CPU à 4 GHz, RAM à 250 MHz). C'est le facteur de 256. La RAM d'un processeur moderne ressemble à un RL02 d'un PDP-11, presque comme une unité de stockage périphérique! :) Pas aussi simple, naturellement, mais tout ce qui frappe dans le cache sera écrit, et des écritures inutiles priveraient d'autres calculs de cet espace de cache toujours aussi important.
kkm
@kkm: Oui, gaspiller environ 2x 128ko d'empreinte de cache L3 sur un tampon de pipe dans le noyau et un tampon de lecture dans le nullprogramme serait nul, mais la plupart des processeurs multicœurs ne fonctionnent pas avec tous les cœurs occupés en permanence. Le temps CPU pour exécuter le nullprogramme est généralement gratuit. Sur un système avec tous les noyaux ancrés, une tuyauterie inutile est un plus gros problème. Mais non, un tampon "à chaud" peut être réécrit plusieurs fois sans passer à la RAM. Nous concourons donc principalement pour la bande passante L3, pas pour le cache. Pas génial, surtout sur un système SMT (hyperthreading) où d'autres noyaux logiques sur le même physique sont en compétition ...
Peter Cordes
.... Mais votre calcul de mémoire est très imparfait. Les CPU modernes ont beaucoup de parallélisme mémoire, alors même si le temps de latence vers la DRAM est de l'ordre de 200 à 400 cycles d'horloge principaux et que L3> 40, la bande passante est d'environ 8 octets / horloge. (Étonnamment, la bande passante mono-threadée en L3 ou en DRAM est pire sur un Xeon multi-cœur avec une mémoire quadri-canal par rapport à un bureau quadri-cœur, car elle est limitée par la simultanéité maximale des requêtes qu'un noyau peut garder en vol. Bande passante = max_concurrency / latency: Pourquoi Skylake est-il tellement meilleur que Broadwell-E pour un débit de mémoire à un seul thread? )
Peter Cordes
... Voir aussi 7-cpu.com/cpu/Haswell.html pour les chiffres de Haswell comparant un quadricœur à 18 cœurs. Quoi qu'il en soit, oui, les processeurs modernes peuvent effectuer une quantité de travail ridicule par horloge s'ils ne sont pas en attente de mémoire. Vos chiffres semblent ne représenter que 2 opérations ALU par horloge, comme peut-être un Pentium de 1993 ou un ARM moderne à double émission et bas de gamme. Un Ryzen ou Haswell exécute potentiellement 4 opérations scalaires d'ALU entières scalaires + 2 opérations de mémoire par cœur et par horloge, ou bien davantage avec SIMD. Par exemple, Skylake-AVX512 a (par cœur) un débit de 2 par horloge sur vpaddd zmm: 16 éléments de 32 bits par instruction.
Peter Cordes
7

En plus de "tout est un fichier" et donc de la facilité d'utilisation partout où la plupart des autres réponses sont basées, il y a aussi un problème de performances mentionné par @ user5626466.

Pour montrer en pratique, nous allons créer un programme simple appelé nullread.c:

#include <unistd.h>
char buf[1024*1024];
int main() {
        while (read(0, buf, sizeof(buf)) > 0);
}

et compiler avec gcc -O2 -Wall -W nullread.c -o nullread

(Remarque: nous ne pouvons pas utiliser lseek (2) sur les tuyaux, le seul moyen de drainer le tuyau consiste à le lire jusqu'à ce qu'il soit vide).

% time dd if=/dev/zero bs=1M count=5000 |  ./nullread
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 9,33127 s, 562 MB/s
dd if=/dev/zero bs=1M count=5000  0,06s user 5,66s system 61% cpu 9,340 total
./nullread  0,02s user 3,90s system 41% cpu 9,337 total

alors qu'avec /dev/nullla redirection de fichiers standard , nous obtenons des vitesses bien meilleures (en raison des faits mentionnés: moins de changements de contexte, le noyau ignore simplement les données au lieu de les copier, etc.):

% time dd if=/dev/zero bs=1M count=5000 > /dev/null
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 1,08947 s, 4,8 GB/s
dd if=/dev/zero bs=1M count=5000 > /dev/null  0,01s user 1,08s system 99% cpu 1,094 total

(cela devrait être un commentaire là-bas, mais c'est trop gros pour ça et serait complètement illisible)

Matija Nalis
la source
Sur quel matériel avez-vous testé? 4,8 Go / s est assez faible comparé aux 23 Go / s que j’obtiens sur un Skylake i7-6700k (DDR4-2666, mais la mémoire tampon devrait rester chaude dans le cache L3. Une bonne partie du coût est donc coûteuse des appels système avec Spectre. Cela fait plus de tort à la tuyauterie, car les tampons de tuyauterie sont inférieurs à 1 Mo, ce qui représente davantage d'appels système en écriture / lecture. Une différence de performances presque 10x est pire que ce à quoi je m'attendais. Sur mon système Skylake, cette vitesse est de 23 Go / s. 3,3 Go / s , sous x86-64 Linux 4.15.8-1-ARCH, un facteur de 6,8. Wow, les appels système sont chers maintenant)
Peter Cordes
1
@PeterCordes 3 Go / s avec des tampons de canal 64k suggèrent 2x 103124 appels système par seconde ... et ce nombre de changements de contexte, heh. Sur un processeur de serveur, avec 200 000 appels système par seconde, vous pourriez vous attendre à une surcharge d'environ 8% de PTI, car le nombre de postes de travail est très faible. (Le graphique que je référence n'inclut pas l'optimisation PCID, mais ce n'est peut-être pas si important pour les petits ensembles de travail). Donc, je ne suis pas sûr que PTI ait un impact important là-bas? brendangregg.com/blog/2018-02-09/…
sourcejedi
1
Oh intéressant, donc c'est un Silvermont avec 2 Mo de cache L2 , donc votre ddtampon + tampon de réception ne convient pas; vous avez probablement affaire à une bande passante en mémoire plutôt qu’à une bande passante en cache de dernier niveau. Vous pourriez obtenir une meilleure bande passante avec des buffers de 512k ou même de 64k. ( straceSur mon bureau, writeet readretournent 1048576, je pense donc que cela signifie que nous ne payons que le coût utilisateur du noyau <-> pour l'invalidation de TLB + le vidage de prédiction de branche une fois par MiB, et non par 64k, @sourcejedi. C'est Spectre ce qui a le plus coûté, je pense)
Peter Cordes
1
@sourcejedi: Lorsque l'atténuation Specter est activée, le coût d'un syscallretour immédiat ENOSYSest d'environ 1 800 cycles sur Skylake lorsque l'atténuation Specter est activée, la plupart d'entre eux étant l' invalidité dewrmsr la BPU, selon les tests de @ BeeOnRope . Lorsque l'atténuation est désactivée, le temps d'aller-retour utilisateur-> noyau-> utilisateur est d'environ 160 cycles. Mais si vous êtes toucher beaucoup de mémoire, l' atténuation Meltdown est importante aussi. Hugepages devrait aider (moins d'entrées TLB doivent être rechargées).
Peter Cordes
1
@PeterCordes Sur un système unix simple cœur, nous verrions sûrement un commutateur de contexte par 64 Ko, ou quel que soit votre tampon de canal, et cela ferait mal ... en fait, je vois aussi le même nombre de commutateurs de contexte avec 2 cœurs de processeur. ( unix.stackexchange.com/questions/439260/… ); il doit également compter un cycle veille / réveil pour chaque 64k comme un commutateur de contexte (vers un "processus inactif" nominal). Conserver les processus en pipeline sur le même processeur a en réalité fonctionné plus de deux fois plus vite.
sourcejedi
6

Votre question est posée comme si quelque chose serait gagné peut-être dans la simplicité en utilisant un programme nul à la place d'un fichier. Peut-être pourrions-nous nous débarrasser de la notion de "fichiers magiques" et utiliser à la place des "canaux ordinaires".

Mais considérons qu'un tuyau est aussi un fichier . Ils ne sont normalement pas nommés et ne peuvent donc être manipulés que par leurs descripteurs de fichier.

Considérez cet exemple quelque peu artificiel:

$ echo -e 'foo\nbar\nbaz' | grep foo
foo

En utilisant la substitution de processus de Bash, nous pouvons accomplir la même chose de manière plus détournée:

$ grep foo <(echo -e 'foo\nbar\nbaz')
foo

Remplacez le greppour echoet on peut voir sous les couvertures:

$ echo foo <(echo -e 'foo\nbar\nbaz')
foo /dev/fd/63

La <(...)construction est simplement remplacée par un nom de fichier, et grep pense que tout fichier ancien est ouvert, il se trouve que son nom est nommé /dev/fd/63. Voici /dev/fdun répertoire magique qui crée des canaux nommés pour chaque descripteur de fichier possédé par le fichier qui y accède.

Nous pourrions faire moins de magie mkfifoen faisant un tuyau nommé qui apparaît dans lset tout, comme un fichier ordinaire:

$ mkfifo foofifo
$ ls -l foofifo 
prw-rw-r-- 1 indigo indigo 0 Apr 19 22:01 foofifo
$ grep foo foofifo

Autre part:

$ echo -e 'foo\nbar\nbaz' > foofifo

et voici, grep va sortir foo.

Je pense qu'une fois que vous avez compris que les pipes, les fichiers ordinaires et les fichiers spéciaux tels que / dev / null ne sont que des fichiers, il est évident que la mise en oeuvre d'un programme null est plus complexe. Le noyau doit gérer les écritures dans un fichier de toute façon, mais dans le cas de / dev / null, il peut simplement laisser tomber les écritures sur le sol, tandis qu'avec un tube il doit réellement transférer les octets vers un autre programme, qui doit ensuite effectivement les lire.

Phil Frost
la source
@ Lyle Ouais? Alors pourquoi echo est-il imprimé /dev/fd/63?
Phil Frost
Hm. Bon point. Eh bien, ceci est mis en œuvre par des obus, alors peut-être que votre obus est différent de l'ancien obus Bourne avec lequel j'ai grandi.
Lyle
Une différence est que echo ne lit pas stdin, contrairement à grep, mais je ne vois pas comment le shell pourrait le savoir avant de les exécuter.
Lyle
1
Et strace rend cela plus clair: pour moi. vous l'avez exactement, avec bash. La construction '<(...)' fait quelque chose de très différent de <nomfichier. Hm. J'ai appris quelque chose.
Lyle
0

Je dirais qu'il existe un problème de sécurité allant au-delà des paradigmes et des performances historiques. Limiter le nombre de programmes avec des informations d'identification d'exécution privilégiées, aussi simple soit-il, est un principe fondamental de la sécurité du système. Un remplaçant /dev/nullserait certainement nécessaire pour disposer de tels privilèges en raison de son utilisation par les services du système. Les frameworks de sécurité modernes font un excellent travail de prévention des exploits, mais ils ne sont pas infaillibles. Un périphérique piloté par le noyau et accessible en tant que fichier est beaucoup plus difficile à exploiter.

NickW
la source
Cela ressemble à un non-sens. Écrire un pilote de noyau sans bogue n’est pas plus facile que d’écrire un programme sans bogue qui lit + supprime son stdin. Il n'a pas besoin d'être setuid ou quoi que ce soit, donc pour les deux /dev/nullou un programme rejet d' entrée proposé, le vecteur d'attaque serait le même: obtenir un script ou un programme qui fonctionne en tant que root pour faire quelque chose de bizarre (comme essayer de lseekdans /dev/nullou ouvrir plusieurs fois dans le même processus, ou IDK quoi, ou invoquer /bin/nullavec un environnement étrange, ou autre chose).
Peter Cordes
0

Comme d'autres l'ont déjà souligné, /dev/null est un programme constitué d'une poignée de lignes de code. C'est juste que ces lignes de code font partie du noyau.

Pour être plus clair, voici l’implémentation Linux: un périphérique caractère appelle des fonctions lorsqu’il est lu ou écrit. Écrire pour /dev/nullappeler write_null , tout en lisant les appels read_null , enregistrés ici .

Littéralement une poignée de lignes de code: ces fonctions ne font rien. Vous aurez besoin de plus de lignes de code que de doigts sur vos mains uniquement si vous comptez des fonctions autres que lire et écrire.

Matthieu Moy
la source
J'aurais peut-être dû l'exprimer plus précisément. Je voulais dire pourquoi l'implémenter en tant que périphérique char à la place d'un programme. Ce serait quelques lignes dans les deux sens mais la mise en œuvre du programme serait décidément plus simple. Comme d'autres réponses l'ont souligné, cela présente de nombreux avantages. le chef de l'efficacité et de la portabilité parmi eux.
Ankur S
Sûr. Je viens d'ajouter cette réponse parce que voir la mise en œuvre réelle était amusant (je l'ai découverte moi-même récemment), mais la vraie raison est ce que d'autres ont souligné.
Matthieu Moy
Moi aussi! J'ai récemment commencé à étudier les périphériques sous Linux et les réponses ont été assez instructives
Ankur S
-2

J'espère que vous connaissez également le répertoire / dev / chargen / dev / zero et d’autres, comme / dev / null.

LINUX / UNIX en propose quelques-uns - mis à disposition pour que les utilisateurs puissent bien utiliser les fragments de code WELL WRITTEN CODE FrAGMEnTS.

Chargen est conçu pour générer un motif spécifique et répété de caractères - il est assez rapide et repousserait les limites des périphériques série et aiderait à déboguer les protocoles série écrits et ayant échoué à un test ou à un autre.

Zero est conçu pour peupler un fichier existant ou générer beaucoup de zéros.

/ dev / null est juste un autre outil avec la même idée en tête.

Tous ces outils de votre boîte à outils signifient que vous avez une chance sur deux de faire en sorte qu'un programme existant fasse quelque chose d'unique, indépendamment de son (votre besoin spécifique) en tant que périphériques ou fichiers de remplacement.

Permet de mettre en place un concours pour voir qui peut produire le résultat le plus excitant compte tenu du nombre limité de périphériques de personnage dans votre version de LINUX.

utile
la source