Il y a des fichiers spéciaux sous Linux qui ne sont pas vraiment des fichiers.
Les exemples les plus notables et clairs de ceux-ci sont dans le dossier dev
, "fichiers" comme:
/dev/null
- Ignore tout ce que vous écrivez dans le fichier/dev/random
- Sort des données aléatoires au lieu du contenu d'un fichier/dev/tcp
- Envoie toutes les données que vous écrivez dans ce fichier sur le réseauTout d'abord, quel est le nom de ces types de "fichiers" qui sont vraiment une sorte de script ou de binaire déguisé?
Deuxièmement, comment sont-ils créés? Ces fichiers sont-ils intégrés au système au niveau du noyau, ou existe-t-il un moyen de créer vous-même un "fichier magique" (que diriez-vous d'un /dev/rickroll
)?
/dev/zero
est un exemple de "fichier spécial" - en particulier, un "nœud de périphérique". Normalement, ceux-ci sont créés par le processus d'installation de la distribution, mais vous pouvez totalement les créer vous-même si vous le souhaitez.
Si vous demandez ls
à propos de /dev/zero
:
# ls -l /dev/zero
crw-rw-rw- 1 root root 1, 5 Nov 5 09:34 /dev/zero
Le "c" au début vous indique qu'il s'agit d'un "périphérique de caractères"; l'autre type est "bloc périphérique" (imprimé par ls
comme "b"). Très grosso modo, les périphériques à accès aléatoire comme les disques durs ont tendance à être des périphériques de type bloc, tandis que les éléments séquentiels comme les lecteurs de bande ou votre carte son sont généralement des périphériques de caractères.
La partie "1, 5" est le "numéro de périphérique principal" et le "numéro de périphérique mineur".
Avec ces informations, nous pouvons utiliser la commande mknod
pour créer notre propre nœud de périphérique:
# mknod foobar c 1 5
Cela crée un nouveau fichier nommé foobar
, dans le dossier actuel, qui fait exactement la même chose que /dev/zero
. (Vous pouvez bien sûr lui attribuer différentes autorisations si vous le souhaitez.) Tout ce "fichier" contient vraiment les trois éléments ci-dessus - type d'appareil, numéro majeur, numéro mineur. Vous pouvez utiliser ls
pour rechercher les codes d'autres appareils et les recréer également. Lorsque vous vous ennuyez, utilisez simplement rm
pour supprimer les nœuds de périphérique que vous venez de créer.
Fondamentalement, le nombre majeur indique au noyau Linux à quel pilote de périphérique parler, et le nombre mineur indique au pilote de périphérique de quel périphérique vous parlez. (Par exemple, vous avez probablement un contrôleur SATA, mais peut-être plusieurs disques durs y sont connectés.)
Si vous voulez inventer de nouveaux périphériques qui font quelque chose de nouveau ... eh bien, vous devrez éditer le code source du noyau Linux et compiler votre propre noyau personnalisé. Alors ne faisons pas ça! :-) Mais vous pouvez ajouter des fichiers de périphérique qui dupliquent ceux que vous avez déjà très bien. Un système automatisé comme udev surveille simplement les événements de l'appareil et appelle automatiquement mknod
/rm
pour vous. Rien de plus magique que ça.
Il existe encore autre types de fichiers spéciaux:
Linux considère un répertoire comme un type spécial de fichier. (Habituellement, vous ne pouvez pas ouvrir directement un répertoire, mais si vous le pouviez, vous trouveriez que c'est un fichier normal qui contient des données dans un format spécial et indique au noyau où trouver tous les fichiers de ce répertoire.)
Un lien symbolique est un fichier spécial. (Mais un lien dur ne l'est pas.) Vous pouvez créer des liens symboliques en utilisant le ln -s
commande. (Recherchez la page de manuel pour cela.)
Il y a aussi une chose appelée "pipe nommée" ou "FIFO" (file d'attente premier entré, premier sorti). Vous pouvez en créer un avec mkfifo
. A FIFO est un fichier magique qui peut être ouvert par deux programmes à la fois - une lecture, une écriture. Lorsque cela se produit, cela fonctionne comme un pipe Shell normal. Mais vous pouvez démarrer chaque programme séparément ...
Un fichier qui n'est en aucun cas "spécial" est appelé "fichier normal". Vous en verrez parfois la mention dans la documentation Unix. C'est ce que ça signifie; un fichier qui n'est pas un nœud de périphérique ou un lien symbolique ou autre. Juste un fichier quotidien normal sans propriétés magiques.
La plupart /dev
les entrées sont des inodes de périphérique de bloc ou des inodes de périphérique de caractère. Wikipedia a beaucoup de détails à ce sujet, que je ne vais pas répéter.
Mais /dev/tcp
qui est mentionné dans votre question n'est expliqué par aucune des réponses existantes. /dev/tcp
et /dev/udp
sont différents de la plupart des autres /dev
entrées. Les périphériques bloc et caractère sont implémentés par le noyau, mais /dev/tcp
et /dev/udp
sont implémentés en mode utilisateur.
Le shell bash est un programme qui a une implémentation de /dev/tcp
et /dev/udp
(copié de ksh93
). Lorsque vous essayez d'ouvrir un chemin sous ceux avec des opérateurs de redirection bash, il n'exécutera pas un appel système open
ordinaire. Au lieu de cela, bash créera un socket TCP et le connectera au port spécifié.
Cela est implémenté en mode utilisateur et uniquement dans certains programmes, comme le montre l'exemple suivant, qui montre la différence entre laisser bash
et cat
essayer d'ouvrir /dev/tcp/::1/22
$ cat /dev/tcp/::1/22
cat: /dev/tcp/::1/22: No such file or directory
$ cat < /dev/tcp/::1/22
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3
Une différence avec ksh93
est que bash
ne fera que ces connexions TCP avec des opérateurs de redirection, pas aux autres endroits où il peut ouvrir des fichiers comme source
ou .
intégré.
En plus des nœuds de périphériques expliqués dans d'autres réponses (créés avec mknod (2) ou fournis par certains devfs ), Linux a d'autres fichiers "magiques" fournis par special systèmes de fichiers virtuels , en particulier dans /proc/
(voir proc (5) , lire procfs ) et dans /sys/
(lire sur sysfs ).
Ces pseudo-fichiers (qui apparaissent de -e.g. à stat (2) - en tant que fichiers ordinaires, pas en tant que périphériques) sont une vue virtuelle fournie par le noyau; en particulier, la lecture de /proc/
(par exemple avec cat /proc/$$/maps
, ou par open (2) - ing /proc/self/status
dans votre programme) n'implique généralement pas d'E/S physiques à partir du disque ou du réseau, donc est assez rapide.
Pour créer un pseudo-fichier supplémentaire dans /proc/
vous devriez généralement écrire votre propre module du noya et le charger (voir par exemple this ).
Ils sont appelés nœuds de périphérique et sont créés manuellement avec mknod
ou automatiquement par udev
. Ce sont généralement des interfaces de type fichier vers des périphériques de type caractère ou bloc avec des pilotes dans le noyau - par exemple les disques sont des périphériques de bloc, les ttys et les ports série, etc. sont des périphériques de caractères.
Il existe également d'autres types de fichiers "spéciaux", notamment des canaux nommés et des fifos et des sockets.
Comme d'autres utilisateurs l'ont déjà expliqué en détail, les fichiers spéciaux nécessitent du code pour les sauvegarder. Cependant, personne ne semble avoir mentionné que Linux propose plusieurs façons d'écrire ce code dans l'espace utilisateur:
A. Fuse (Système de fichiers dans USErspace) vous permet d'écrire quelque chose comme /proc
sans risque de planter le noyau et faites-le dans la langue/runtime de votre choix, comme Go , Node.js , Perl , PHP , Python , Ruby , Rust , etc. .
Il présente également l'avantage que les systèmes de fichiers Fuse peuvent être montés sans Sudo
car ils s'exécutent en tant qu'utilisateur effectuant le montage.
Voici quelques exemples de choses que les gens ont écrites en utilisant Fuse:
B. Si vous souhaitez créer un périphérique d'entrée virtuel comme un clavier, une souris, un joystick, etc. (par exemple pour écrire un pilote d'espace utilisateur pour une clé USB) appareil auquel vous parlez en utilisant libusb
), il y a input .
Les liaisons sont plus difficiles à trouver, mais je sais qu'elles existent pour Aller (Clavier uniquement), Python et Ruby(2) .
Voici des exemples d'utilisation de données d'entrée réelles:
C. Pour les périphériques de caractères génériques, il y a CUSE (périphériques de caractères dans USErspace) . C'est beaucoup moins populaire cependant.
Le seul utilisateur de l'API CUSE que je connaisse personnellement est le même programme qui a provoqué sa création: osspd , qui implémente /dev/dsp
, /dev/adsp
, et /dev/mixer
(l'API audio OSS) dans l'espace utilisateur afin qu'ils puissent être routés via PulseAudio ou dmix.
La seule liaison CUSE que j'ai pu trouver est cusepy , qui n'a pas été mise à jour depuis 2010.
D. Vous n'avez peut-être pas du tout besoin d'un nouveau fichier spécial.
Par exemple, vous pouvez ouvrir une communication brute avec n'importe quel périphérique USB en utilisant libusb (Liste des liaisons sur la page), puis communiquer avec d'autres programmes via un autre mécanisme (sockets TCP/UDP, lecture/écriture stdin/stdout ou fichiers normaux sur disque, etc.).
Le livre Linux Device Drivers (hautement recommandé) explique cela en détail, et vous a même créé un module de noyau qui le fait à titre d'exemple, mais en un mot, chaque pilote de périphérique a des fonctions spécifiques qui sont appelées lorsqu'un fichier est ouvert, fermé, lu, écrit, etc. Les fichiers "spéciaux" font juste quelque chose de spécial à l'intérieur de ces fonctions, au lieu d'accéder au matériel de stockage sur un disque.
Par exemple, la fonction d'écriture pour /dev/null
ne fait rien, ignorant les octets. La fonction de lecture pour /dev/random
renvoie un nombre aléatoire.
mount -t devtmpfs
Il est également intéressant de voir que dans les systèmes modernes, /dev
est normalement un type de système de fichiers qui peut être monté où vous le souhaitez. Ubuntu 16.04:
mkdir d
Sudo mount -t devtmpfs none d
head -c 10 d/random
Sudo umount d
Ceci est activé par CONFIG_DEVTMPFS=y
, et permet au noyau lui-même de créer et de détruire les fichiers de périphérique selon les besoins.
CONFIG_DEVTMPFS_MOUNT=y
Cette option permet au noyau de monter automatiquement devtmpfs sur /dev
.
drivers/base/Kconfig
documents:
config DEVTMPFS_MOUNT
bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs"
depends on DEVTMPFS
help
This will instruct the kernel to automatically mount the
devtmpfs filesystem at /dev, directly after the kernel has
mounted the root filesystem. The behavior can be overridden
with the commandline parameter: devtmpfs.mount=0|1.
This option does not affect initramfs based booting, here
the devtmpfs filesystem always needs to be mounted manually
after the rootfs is mounted.
With this option enabled, it allows to bring up a system in
rescue mode with init=/bin/sh, even when the /dev directory
on the rootfs is completely empty.
file_operations
Enfin, vous devez créer votre propre module de noyau de périphérique de caractères pour voir exactement ce qui se passe.
Voici un exemple exécutable minimal: Comprendre les fichiers de périphériques de caractères (ou de caractères spéciaux)
L'étape la plus importante consiste à configurer le file_operations
struct, par exemple:
static const struct file_operations fops = {
.owner = THIS_MODULE,
.read = read,
.open = open,
};
static int myinit(void)
{
major = register_chrdev(0, NAME, &fops);
return 0;
}
qui contient des pointeurs de fonction qui sont appelés pour chaque appel système lié au fichier.
Il devient alors évident que vous remplacez ces appels système liés aux fichiers pour faire ce que vous voulez, et c'est ainsi que le noyau implémente des périphériques comme /dev/zero
.
Créer /dev
entrées automatiquement sans mknod
Le dernier mystère est de savoir comment le noyau crée automatiquement /dev
entrées.
Le mécanisme peut être observé en créant un module de noyau qui le fait vous-même comme indiqué sur: https://stackoverflow.com/questions/5970595/how-to-create-a-device-node-from-the- init-module-code-of-a-linux-kernel-module/45531867 # 45531867 et revient à un device_create
appel.