Ce ne sont en fait que des interfaces. Encodés par un nombre "majeur" et "mineur", ils fournissent un crochet au noyau.
Ils existent en deux versions (enfin, trois, mais les canaux nommés sont hors de portée de cette explication pour l'instant): les périphériques de caractères et les périphériques de blocs.
Les périphériques de bloc ont tendance à être des périphériques de stockage, capables de mettre en tampon la sortie et de stocker des données pour une récupération ultérieure.
Les périphériques de caractères sont des éléments tels que des cartes audio ou graphiques, ou des périphériques d'entrée comme le clavier et la souris.
Dans chaque cas, lorsque le noyau charge le bon pilote (soit au démarrage, soit via des programmes comme udev ), il analyse les différents bus pour voir si des périphériques gérés par ce pilote sont réellement présents sur le système. Si tel est le cas, il configure un appareil qui «écoute» le numéro majeur / mineur approprié.
(Par exemple, le processeur de signal numérique de la première carte audio trouvée par votre système obtient la paire de nombres majeurs / mineurs de 14/3; la seconde obtient 14,35, etc.)
Il appartient à udev de créer une entrée dans /dev
named en dsp
tant que périphérique de caractère marqué majeur 14 mineur 3.
(Dans les versions beaucoup plus anciennes ou à empreinte minimale de Linux, il se /dev/
peut qu'il ne soit pas chargé dynamiquement mais contienne simplement tous les fichiers de périphérique possibles de manière statique.)
Ensuite, lorsqu'un programme de l'espace utilisateur essaie d'accéder à un fichier marqué comme un `` fichier spécial de caractères '' avec le numéro majeur / mineur approprié (par exemple, votre lecteur audio tente d'envoyer de l'audio numérique à /dev/dsp
), le noyau sait que ces données doivent être transmis via le conducteur auquel est attaché un numéro majeur / mineur; on peut supposer que le conducteur sait à son tour quoi en faire.
Chaque fichier, périphérique ou autre, prend en charge 6 opérations de base dans le VFS:
De plus, les fichiers de périphérique prennent en charge le contrôle des E / S, ce qui permet d'autres opérations diverses non couvertes par le premier 6.
Dans le cas d'un caractère spécial, la recherche et la révélation ne sont pas implémentées car elles prennent en charge une interface de streaming . Autrement dit, lire ou écrire directement comme cela se fait avec la redirection dans le shell:
la source
file_operations
Exemple exécutable minimalUne fois que vous voyez un exemple minimal, tout devient évident.
Les idées clés sont:
file_operations
contient les rappels pour chaque appel système lié au fichiermknod <path> c <major> <minor>
crée un dispositif de caractères qui utilise ceuxfile_operations
cat /proc/devices
character_device.ko
module du noyau:Programme de test Userland:
GitHub QEMU + Buildroot en amont avec passe-partout pour le faire fonctionner:
Exemples plus complexes:
read
,write
,lseek
Avec un tampon interne de taille fixe et sur debugfs à la place d'un dispositif de caractères: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/fops.cpoll
: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.cioctl
: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.canon_inode_getfd
associe unfile_operations
à un descripteur de fichier sans aucun fichier de système de fichiers: /programming/4508998/what-is-anonymous-inode/44388030#44388030la source
*off = 1;
et pourquoi est-il réglé sur1
?read
appels au mêmeopen(
descripteur de fichier. Le conducteur peut en faire ce qu'il veut. La sémantique habituelle est de contenir le nombre d'octets lus. Dans cet exemple cependant, nous avons juste une sémantique plus simple:0
pour la première lecture,1
après la première lecture. Essayez de l'exécuter et mettez une étape printk ou GDB à le déboguer."Caractère à la fois" est un terme impropre (tout comme l'idée que les dispositifs de caractères ne prennent pas nécessairement en charge la recherche et la révélation). En fait, les périphériques "bloc à la fois" (c'est-à-dire strictement orientés vers l'enregistrement, comme un lecteur de bande *) doivent être des périphériques de caractères. Il en va de même pour l'idée qu'un périphérique de caractères doit nécessairement être impossible à rechercher - les pilotes de périphériques de caractères définissent une
file_operations
structure complète qui est libre de définir llseek ou non selon que le périphérique prend en charge l'opération. Les appareils de caractères que la plupart des gens considèrent comme des exemples sont null, urandom, les appareils TTY, la carte son, la souris, etc. et / dev / kmem sont également des périphériques de caractères et ils sont tous recherchables.Comme je l'ai mentionné, un pilote de périphérique de caractères définit une structure file_operations qui a des pointeurs de fonction pour toutes les opérations que quelqu'un pourrait vouloir appeler sur un fichier - rechercher, lire, écrire, ioctl, etc. - et ceux-ci sont chacun appelés une fois lorsque l'appel système correspondant est exécuté avec ce fichier de périphérique ouvert. Et lire et écrire peuvent donc faire tout ce qu'ils veulent avec leurs arguments - ils peuvent refuser d'accepter une écriture trop grande ou seulement écrire ce qui convient; il ne peut lire que les données correspondant à un enregistrement plutôt que le nombre entier d'octets demandé.
Alors, qu'est-ce qu'un périphérique bloc, alors? Fondamentalement, les périphériques de bloc sont des unités de disque. Aucun autre type de périphérique (à l'exception des lecteurs de disques virtuels , comme le disque virtuel et le bouclage) n'est un périphérique bloc. Ils sont intégrés dans le système de demande d'E / S, la couche du système de fichiers, le système de tampon / cache et le système de mémoire virtuelle d'une manière qui ne le sont pas, même lorsque vous accédez par exemple à / dev / sda à partir d'un processus utilisateur. . Même les «périphériques bruts» que cette page mentionne comme exception sont des périphériques de caractères .
* Certains systèmes UNIX ont implémenté ce qui est maintenant appelé "mode de bloc fixe" - qui permet au groupe du noyau et de diviser les demandes d'E / S de s'adapter aux limites de bloc configurées de la même manière que pour les unités de disque - en tant que bloc dispositif. Un dispositif de caractères est nécessaire pour le "mode bloc variable", qui préserve les limites des blocs du programme utilisateur, car un seul appel en écriture (2) écrit un bloc et un seul appel en lecture (2) renvoie un bloc. Étant donné que le changement de mode est désormais implémenté sous la forme d'un ioctl plutôt que d'un fichier de périphérique distinct, un périphérique de caractères est utilisé. Les lecteurs de bande à enregistrement variable sont pour la plupart "non recherchables" car la recherche implique le comptage d'un certain nombre d'enregistrements plutôt que d'un certain nombre d'octets, et l'opération de recherche native est implémentée comme un ioctl.
la source
Les périphériques de caractères peuvent être créés par des modules du noyau (ou le noyau lui-même). Lorsqu'un périphérique est créé, le créateur fournit des pointeurs vers des fonctions qui implémentent des appels standard tels que open, read, etc. Le noyau Linux associe ensuite ces fonctions au périphérique de caractères, par exemple lorsqu'une application en mode utilisateur appelle read () sur un fichier de périphérique de caractères, il en résultera un appel système, puis le noyau acheminera cet appel vers une fonction de lecture spécifiée lors de la création du pilote. Il y a un tutoriel étape par étape sur la création d'un périphérique de personnage ici , vous pouvez créer un exemple de projet et l'étape à travers celui-ci à l'aide d'un débogueur pour comprendre comment l'objet périphérique est créé et quand les gestionnaires sont appelés.
la source