Programmation Noyau Sous Linux . Pilotes En Mode Caractere.pdf
(
421 KB
)
Pobierz
Programmation noyau sous Linux Pilotes en mode
caractère
Ce nouvel article de la série est consacré à la programmation des pilotes de périphériques en mode
dit " caractère ". Les connaissances acquises lors du premier article consacré à l’API des modules
Linux vont nous permettre d’aborder assez simplement la notion de " pilote Linux " qui, finalement,
est un module dans une version un peu plus ardue.
Nous aborderons les sujets suivants :
•
•
•
•
•
•
•
l’introduction aux pilotes Linux : les différents types de pilotes ;
l’interface avec l’espace utilisateur : le répertoire /dev ;
l’API des pilotes en mode caractère ;
le transfert de données avec l’espace utilisateur ;
l’allocation dynamique de mémoire ;
la méthode ioctl ;
les files d’attente.
Les exemples fournis sont inspirés de ceux développés par Stelian Pop pour la formation " noyau
Linux " d’Open Wide.
Introduction aux pilotes Linux
Le système Linux étant de la famille UNIX, la structure d’un pilote Linux est relativement proche
de celle d’un pilote générique UNIX. Un pilote est une portion de code exécutée dans l’espace du
noyau. Il est chargé de faire l’interface entre un programme utilisateur et un composant matériel. Ce
dernier point n’est pas forcément vrai puisqu’il existe des pilotes de périphériques virtuels tels les
systèmes de fichier. En toute rigueur, un programme UNIX devrait systématiquement accéder à un
périphérique au travers d’un pilote. Ce n’est pas toujours le cas, puisque nous avons vu dans
plusieurs articles précédents qu’il était possible – sous Linux – d’accéder à une ressource matérielle
grâce à la fonction ioperm(), puis aux fonctions inb() et outb(). Cette méthode est cependant peu
recommandée et rarement utilisée sauf pour des cas simples ou très particuliers.
L’API d’un pilote Linux est très fortement inspirée de celle des pilotes UNIX des années 1970. Nous
rappelons qu’elle est basée sur l’utilisation de fonctions ou méthodes permettant d’effectuer des
actions basiques.
•
•
•
•
•
une méthode open() permettant d’ouvrir le périphérique ;
une méthode close() permettant de fermer (libérer) le périphérique. Sous Linux, cette
méthode est nommée release() ;
une méthode read() permettant de lire des données du périphérique ;
une méthode write() permettant d’écrire des données sur le périphérique ;
une méthode ioctl() (Input Output ConTroL) permettant de configurer le périphérique.
Il existe d’autres méthodes comme lseek(), poll() ou mmap(), mais elles sont moins fréquemment
utilisées dans les pilotes simples.
Les méthodes sont activées depuis un programme de l’espace utilisateur en passant par les fichiers
spéciaux du répertoire /dev. Ces fichiers sont appelés nœuds (nodes ou devices en anglais). Nous
décrirons plus précisément ce répertoire plus loin dans le document.
Le pilote est conforme à l’API des modules Linux décrite dans l’article précédent. Du point de vue
fonctionnel, l’API des modules n’a rien à voir avec les pilotes. Elle permet simplement de charger
dynamiquement le pilote dans l’espace du noyau en cours d’exécution.
Remarque :
Un pilote externe aux sources du noyau Linux sera toujours développé sous forme de module.
Il existe différents types de pilotes, liés aux types de périphériques qu’ils peuvent contrôler.
•
•
•
Les pilote en mode caractère (char device driver). Ces pilotes sont destinés à manipuler les
périphériques les plus courants avec lesquels ils échangent des données sous forme de flux
d’octets de taille variable (minimum 1 octet). De ce fait, la plupart des pilotes de
périphérique sous Linux seront en mode caractère. L’exemple le plus courant est le pilote de
l’interface série RS232.
Les pilotes en mode bloc (block device driver). Ces pilotes sont destinés à manipuler des
périphériques de stockage avec lesquels ils échangent des blocs de données (disque dur,
CDROM, DVD, disque mémoire, etc.). La taille des blocs peut être de 512, 1024, 2048 (ou
plus) octets suivant le périphérique.
Les pilotes de périphériques réseau (network device driver). Ils sont destinés à gérer des
contrôleurs réseau (exemple : carte Ethernet), mais aussi des piles de protocoles.
Contrairement aux autres pilotes, ils n’utilisent pas d’entrée dans le répertoire /dev.
A cette liste, nous pouvons également ajouter quelques pilotes spéciaux le plus souvent dédiés à des
bus ou des API particulières, citons entre autres :
•
•
•
le bus PCI ;
le bus USB ;
les pilotes vidéo (V4L et V4L2).
La structure générale du noyau Linux est présentée dans la figure cidessous. On peut y voir le
positionnement des principaux types de pilotes (schéma par Julien Gaulmin).
Figure 1 : Structure du noyau Linux
Dans le présent article, nous traiterons uniquement le cas des pilotes en mode caractère.
Quelques règles d’usage
La programmation d’un pilote est notoirement plus complexe que celle d’un programme en espace
utilisateur.
•
•
•
•
•
•
Le noyau est un élément fondamental du système et un pilote mal conçu peut entraîner des
dysfonctionnements importants, voire un arrêt du système, ce qui n’est pas le cas dans
l’espace utilisateur.
La mise au point dans l’espace noyau est complexe (voir l’article " Débogage dans l’espace
noyau avec KGDB " dans le magazine LM 88).
Les fonctions de la glibc ne sont pas disponibles dans l’espace noyau, mais certaines sont
implémentées dans le répertoire lib des sources du noyau.
Un pilote doit être programmé en C (pas de C++), mais la structure d’un pilote est " orientée
objet ".
En toute rigueur, on doit respecter le style de programmation défini dans le fichier
Documentation/CodingStyle des sources du noyau.
Un pilote doit prendre en compte les différentes architectures, particulièrement au niveau des
" bigendians " et " littleendians ".
Tout cela doit inciter le lecteur à concevoir des pilotes les plus simples possibles et de reporter la
difficulté sur l’espace utilisateur. De plus, nous rappelons une nouvelle fois qu’un pilote Linux doit
en théorie être diffusé sous GPL, ce qui peut poser des problèmes par rapport à certains codes
sensibles.
Le point de vue de l’espace utilisateur
Dans le cas d’un pilote en mode caractère, le périphérique est vu depuis un programme utilisateur
comme un fichier spécial du répertoire /dev. Le fichier est dit " spécial ", car il n’occupe quasiment
pas d’espace sur le disque (uniquement le point d’entrée). Le but du fichier spécial est uniquement
de faire un lien entre l’espace utilisateur et le pilote de périphérique associé.
Principe du majeur/mineur
Si l’on regarde le fichier spécial associé au premier port série, on obtient :
$ ls -l /dev/ttyS0
crw-rw---- 1 root uucp 4, 64 oct 31 10:34 /dev/ttyS0
Le premier caractère identifie le type de périphérique, soit ici la lettre c pour caractère. Les neufs
caractères suivants correspondent aux droits d’accès habituels sous Linux. Il en est de même pour le
propriétaire et le groupe.
Par contre, les deux champs qui suivent sont particuliers aux fichiers spéciaux.
•
•
La première valeur appelée " majeur " identifie le type de périphérique (ici le port série).
La seconde valeur appelée " mineur " identifie l’instance du périphérique. Ces mineurs
permettent de gérer plusieurs périphériques identiques avec le même pilote (donc le même
majeur).
La valeur du majeur est unique. Dans le cas de Linux, la liste des majeurs réservés est disponible
dans la documentation des sources du noyau, soit le fichier Documentation/devices.txt. La liste des
majeurs actifs à un instant donné est disponible dans le fichier /proc/devices.
$ cat /proc/devices
Character devices:
1
4
4
4
5
5
...
mem
/dev/vc/0
tty
ttyS
/dev/tty
/dev/console
Remarque :
L’utilisation erronée d’un numéro de majeur réservé entraînerait de fortes perturbations dans le
fonctionnement du système !
Dans le cas de l’ajout d’un nouveau pilote par l’utilisateur, il sera préférable d’utiliser les
fonctionnalités d’allocation dynamique de majeur ou de mineur plutôt que d’utiliser une valeur fixe.
Ce point sera abordé dans la description de l’API.
La création d’un nouveau fichier spécial s’effectue grâce à la commande mknod. Si l’on devait
créer le fichier spécial /dev/ttyS0, on utiliserait la commande :
# mknod /dev/ttyS0 c 4 64
Pour supprimer un fichier spécial, on utilise la commande classique rm.
# rm -f /dev/ttyS0
Limites du système de majeur/mineur
Cette approche est simple, mais elle pose un problème de duplication d’information entre l’espace
utilisateur et l’espace noyau. En effet, les fichiers spéciaux sont créés dans /dev avec des valeurs
passées à mknod ou bien à des utilitaires chargés de créer une liste de fichiers spéciaux (exemple :
MAKEDEV). Pour que le système fonctionne, il faut que ces mêmes valeurs soient utilisées dans le
code source des pilotes.
L’approche devfs permettait d’améliorer les choses pour le noyau 2.4. Grâce à devfs, les
périphériques ne sont plus manipulés sous forme de majeur/mineur, mais sous forme de nom.
L’entrée dans /dev est créée dynamiquement lors de l’insertion du pilote. Cependant le code de
devfs est situé dans le noyau ce qui pose des problèmes de nommage et de compatibilité avec le
standard LSB (Linux Standard Base) qui ne traite pas l’espace noyau.
De ce fait, une autre approche appelée udev est désormais utilisée pour le noyau 2.6 (même si la
compatibilité devfs est conservée). Le principal intérêt de udev est de fonctionner entièrement en
espace utilisateur. Un démon nommé udevd reçoit des instructions du noyau afin de procéder à la
création de l’entrée dans /dev grâce à des règles modifiables par l’administrateur. Techniquement
parlant, udev est basé sur deux composants liés au noyau 2.6.
•
•
hotplug : un démon chargé de la gestion de l’insertion/suppression des périphériques ;
sysfs (monté sur /sys) : un système de fichier virtuel similaire à /proc décrivant les
périphériques connectés au système.
Le lien vers un article complet concernant udev est disponible dans la bibliographie.
API de programmation
Nous avons vu, dans l’article précédent, l’API de programmation des modules Linux. Sachant que,
dans notre cas, un pilote est systématiquement un module, cette API sera utilisée, enrichie de
plusieurs éléments permettant de définir les méthodes décrites au début de l’article.
Comme nous l’avons fait pour les précédents articles et afin de faciliter la compréhension, nous
baserons la démonstration sur un exemple simple et évolutif.
La structure file_operations
Cette structure dont le type est décrit dans le fichier <linux/fs.h> permet de définir les méthodes du
pilote. La tradition veut que l’on préfixe le nom de la structure et des méthodes par le nom du pilote
(ici mydriver).
static struct file_operations mydriver_fops = {
.owner =
THIS_MODULE,
.read = mydriver_read,
.write =
mydriver_write,
.open = mydriver_open,
.release =
mydriver_release,
};
De part la nécessité de définir les symboles correspondant aux méthodes, on placera la déclaration
de la structure après la définition des méthodes. De ce fait, l’architecture générale du pilote sera la
suivante :
1. Déclaration des entêtes.
2. Déclaration des macroinstructions identifiant le pilote.
3. Déclaration des variables.
4. Définition des méthodes.
5. Définition de la structure file_operations.
6. Définition des points d’entrée module_init() et module_exit() du module.
Déclarations à effectuer
Dans le cas simple qui nous intéresse, les déclarations sont similaires à celles que nous avons
effectuées pour l’exemple des modules de l’article précédent.
#include <linux/kernel.h>
/* printk() */
#include <linux/module.h>
/* modules */
#include <linux/init.h> /* module_{init,exit}() */
#include <linux/fs.h>
/* file_operations */
MODULE_DESCRIPTION("mydriver1");
MODULE_AUTHOR("Stelian Pop/Pierre Ficheux, Open Wide");
MODULE_LICENSE(“GPL”);
La seule nouveauté est la présence du fichier d’entête <linux/fs.h> nécessaire à la définition de la
structure file_operations.
Plik z chomika:
musli_com
Inne pliki z tego folderu:
Linux System Programming Talking Directly to the Kernel and C Library.pdf
(8858 KB)
Unix Network Programming Volume 1.pdf
(14024 KB)
Unix Network Programming Volume 2.pdf
(21636 KB)
Developpement d un espiogiciel d evaluation.pdf
(1302 KB)
Developpement avance d un rootkit pour les modules du noyau.pdf
(970 KB)
Inne foldery tego chomika:
3D Design - Programming
ActionScript
Actionscript - Flash - Flex - Air
Ada
ADO
Zgłoś jeśli
naruszono regulamin