Ce chapitre présente les concepts nécessaires à l'installation de cartes filles additionnelles et à la configuration des périphériques complémentaires sous Linux. La procédure d'installation des disques durs, graveurs, cartes son, cartes graphiques, cartes vidéo et cartes réseau sera donc décrite, ainsi que la manière de les configurer pour obtenir un fonctionnement correct sous Linux.
Comme il l'a déjà été expliqué dans les chapitres précédents, Linux gère la plupart des périphériques comme des fichiers spéciaux. De plus, la plupart des gestionnaires de périphériques peuvent être chargés dynamiquement, sous la forme de modules du noyau. Cette section présentera donc les notions de base concernant les modules du noyau, les fichiers spéciaux de périphériques, ainsi que leurs rôles respectifs dans la configuration d'un nouveau matériel. La syntaxe utilisée pour fournir des options au noyau lors de son amorçage et les mécanismes de détection du matériel et de chargement automatique des gestionnaires de périphériques seront également présentés.
Les modules du noyau sont des bibliothèques que l'on peut charger dans le noyau lorsque celui-ci a besoin d'une certaine fonctionnalité. Une fois chargés, les modules font partie intégrante du noyau et ajoutent leurs fonctions à celles existantes. Ces bibliothèques sont normalement stockées dans le répertoire /lib/modules/version/, où version est le numéro de version du noyau pour lequel ces modules ont été créés.
Beaucoup de fonctionnalités du noyau peuvent être configurées pour être utilisées sous la forme de modules. Dans le cas des pilotes de périphériques, les modules permettent de réaliser un noyau générique et de prendre en charge dynamiquement les différents périphériques de l'ordinateur. Certains pilotes ne fonctionnent d'ailleurs que sous la forme de modules, aussi faut-il savoir les manipuler. Les modules sont également fortement utilisés pour la configuration automatique des périphériques connectables à chaud.
Les modules peuvent être chargés manuellement à l'aide des commandes insmod et modprobe. modprobe est un peu plus évoluée, car elle gère les dépendances entre les modules et est capable de charger les modules utilisés par le module demandé. Leur utilisation est des plus simple :
insmod moduleou :
modprobe moduleoù module est le nom du module à charger.
En réalité, la commande modprobe appelle
la commande insmod pour chaque module qui doit être chargé, dans l'ordre des dépendances
des modules. De cette manière, chaque module peut être chargé sans problèmes, car toutes
les fonctionnalités qu'il utilise sont déjà présentes dans le noyau lors de son chargement.
La commande modprobe va chercher les informations de dépendances dans le fichier
modules.dep, situé dans le répertoire
/lib/module/version/. Ce fichier utilise une syntaxe très simple,
et spécifie pour chaque module les modules dont il dépend. Bien qu'il puisse parfaitement être écrit
à la main, cela nécessiterait d'avoir une connaissance approfondie des modules du noyau. C'est donc
pour cela que l'outil depmod a été écrit. Cet outil permet de générer le fichier
modules.dep automatiquement, pourvu qu'on l'appelle avec l'option
-a
en ligne de commande :
La liste de modules qui ont été chargés peut être obtenue aisément avec la commande lsmod :
Enfin, la commande rmmod permet de décharger un module, avec la ligne de commande suivante :
On préférera cependant l'utilisation de la commande modprobe avec l'option-r
pour décharger les modules, car cette commande est capable de décharger récursivement
les modules dont dépendait le module à décharger lorsqu'ils ne sont eux-même plus utilisés. La syntaxe
est alors la suivante :
Note : En réalité, il n'est généralement pas nécessaire de charger les modules du noyau manuellement pour les pilotes de périphériques. En effet, comme nous allons le voir dans les sections suivantes, Linux dispose de mécanismes permettant de réaliser le chargement des modules de pilotes de périphériques automatiquement, lorsque ces pilotes sont détectés. Toutefois, il est possible que certains périphériques ne soient pas pris en charge par ces mécanismes (notamment les vieux périphériques ISA non Plug and Play).
La plupart des modules peuvent prendre un certain nombre d'options lors de leur chargement. Ces options permettent de fixer certains paramètres, et dans le cas des pilotes de périphériques, de préciser la configuration matérielle utilisée.
Les options des modules peuvent être spécifiées en ligne de commande lors de l'appel de insmod ou de modprobe. Toutefois, il est possible d'enregistrer ces options de manière permanente à l'aide du fichier de configuration /etc/modprobe.conf. Lors du chargement d'un module modprobe consulte en effet ce fichier et passe les paramètres indiqués au module à charger.
Afin de faciliter la maintenance du système, modprobe consulte également les fichiers de configuration situés dans le répertoire /etc/modprobe.d/. Ainsi, les options spécifiques à chaque module peuvent être placées dans un fichier de configuration dédié dans ce répertoire.
Le format de ces fichiers de configuration est assez simple, les options de chaque module se définissant sur une ligne commençantpar le mot clé options. Ce mot clé doit être suivi du nom du module pour lequel ces options sont définies, lui-même suivi des paramètres de chargement du module. La syntaxe est donc la suivante :
options module paramètresLa liste des paramètres que l'on peut passer à un module dépend bien évidemment du module. Vous pouvez obtenir la liste des options supportées par un module à l'aide de l'option
-p
de la commande modinfo :
Nous verrons par la suite des exemples de passage de paramètres pour les modules les plus courants.
Les fichiers de configuration de modprobe permettent également de définir la manière dont le chargement et le déchargement des modules doit être faite. Pour cela, il permet de spécifier une ligne de commande complète pour ces opérations. Ces lignes de commandes sont introduites via les mots-clefs install et remove. La syntaxe de ces mots-clés est la suivante :
install module commande remove module commandeoù module est le nom du module à charger ou à décharger, et commande est la ligne de commande de l'opération à effectuer pour cela.
Par exemple, si le port parallèle est utilisé pour accéder à un périphérique nécessitant une opération d'initialisation quelconque, celle-ci peut être exécutée lors du chargement du module à l'aide du mot clé install :
(en supposant que la commande initperiph permet d'initialiser le périphérique en question). Évidemment, ce type d'opération dépend du matériel connecté sur le port parallèle, mais le principe est là. Vous noterez que pour éviter que la commande modprobe ne boucle sur elle-même dans la commande indiquée par la directive install, l'option--ignore-install
lui a été fournie. Dans le cas d'une directive
remove, il faudrait utiliser l'option --ignore-remove
.
Dans la plupart des cas, la définition de ces commandes de chargement et de déchargement sont facultatives, car les modules utilisent généralement un jeu de paramètres implicites qui permettent de les utiliser même si le fichier de configuration modprobe.conf est absent. Vous pouvez visualiser ces options en renommant le fichier de configuration modprobe.conf et en tapant la commande suivante :
Cette commande vous permettra également de recréer un nouveau fichier de configuration si d'aventure vous perdiez celui fourni avec votre distribution.La plupart des fonctionnalités disponibles sous la forme de modules peuvent également être intégrées directement dans le noyau, lors de la compilation de celui-ci. Il est évident qu'il n'est alors pas possible d'utiliser les fichiers de configuration de modprobe pour fixer les valeurs des différentes options que ces fonctionnalités peuvent utiliser. En effet, ces paramètres doivent dans ce cas être définis dès le démarrage du système, et il est nécessaire de les fournir dès le chargement du noyau en mémoire.
Cela se fait, comme nous l'avons vu dans
la Section 3.4.7, en fournissant au noyau les paramètres en question
sur sa ligne de commande. Nous avons déjà vu comment la quantité de mémoire de l'ordinateur
pouvait être passée au noyau à l'aide de l'option mem
si elle n'était pas
détectée correctement. Il est possible de spécifier de la même manière de nombreuses
autres options, en particulier pour activer ou désactiver certains gestionnaires de périphériques
ou pour leur indiquer les paramètres du matériel.
Les options fournies au noyau sur sa ligne de commande par le gestionnaire d'amorçage sont lues par le noyau dès son démarrage, afin de fixer les paramètres qu'elles décrivent jusqu'au redémarrage suivant. Pour certaines fonctionnalités, ces paramètres peuvent toutefois être modifiées dynamiquement après le démarrage, via le système de fichiers virtuel /proc/.
Il existe un grand nombre d'options, et leur syntaxe varie en fonction du sous-système qui les utilise. Le fichier de documentation kernel-parameters.txt du répertoire Documentation/ des sources du noyau vous donnera la liste complète de ces options. Nous verrons quelques-unes de ces options spécifiquement dans la suite du chapitre, lorsque nous décrirons les paramètres des gestionnaires de périphériques.
En revanche, les options des modules intégrés au noyau suivent toujours la même syntaxe. Cette syntaxe est la suivante :
module.paramètre=valeuroù module est le nom du module du noyau qui implémente la fonctionnalité lorsqu'elle est utilisée sous la forme de module, paramètre est le nom de l'option de ce module du noyau telle qu'elle est donnée par la commande modinfo, et valeur est sa valeur. Ainsi, si vous voulez intégrer une fonctionnalité d'un module dans le noyau, il suffit simplement d'ajouter les paramètres complémentaires dans la ligne de commande de celui-ci en fonction des options définies dans les fichiers de configuration de modprobe. Nous verrons la manière de procéder plus loin dans ce chapitre.
Note : Intégrer un pilote de périphérique directement dans le noyau peut être intéressant si ce périphérique est inamovible et que l'on ne veut pas avoir à prendre en charge les modules. Toutefois, ce n'est pas une bonne idée lorsqu'on installe ce périphérique et qu'on cherche à le configurer, ou si l'on désire se réserver la possibilité de faire une mise à jour de ce périphérique sans redémarrer l'ordinateur. En effet, il est souvent nécessaire de redémarrer pour prendre en compte de nouveaux paramètres lorsque le pilote de périphérique est intégré au noyau, alors que lorsqu'il est sous la forme d'un module, un simple déchargement et rechargement suffit.
Par contre, lorsque votre configuration sera finie, vous pourrez parfaitement vous passez des modules et supprimer les modules des pilotes de périphériques dont vous ne disposez pas. Vous gagnerez ainsi de la place disque et potentiellement plusieurs dizaines de secondes au démarrage de votre ordinateur si votre distribution recherche les nouveaux périphériques à chaque démarrage.
La notion de fichier spécial de périphérique simplifie énormément l'utilisation du matériel par les programmes d'application, puisque la plupart des opérations sur un périphérique reviennent simplement à réaliser une écriture ou une lecture. Évidemment, l'écriture sur un fichier spécial de disque permet d'enregistrer les données sur ce disque, et la lecture permet de les récupérer. Mais cela ne s'arrête pas là ! Par exemple, la communication avec le modem se fait simplement en écrivant et en lisant les données sur le fichier spécial du port série sur lequel le modem est connecté. De même, jouer un fichier son revient simplement à l'écrire dans le fichier spécial qui gère la carte son. Il est même possible d'accéder à la mémoire vidéo par l'intermédiaire d'un fichier spécial de périphérique...
Bien entendu, certaines fonctionnalités avancées des périphériques
ne peuvent pas être accédées simplement par une écriture ou une lecture dans un fichier spécial. Le système
fournit donc aux applications d'autres moyens d'accéder à ces fonctionnalités, par l'intermédiaire
d'appels systèmes spécifiques (pour ceux qui sont intéressés par la programmation système, cet appel
système est réalisé par la fonction ioctl
, dont le nom provient de l'abréviation
de l'anglais « Input / Output ConTroL »). Évidemment, cette méthode n'est utilisée que
par les programmes qui connaissent bien le fonctionnement du gestionnaire du périphériques, car
ils doivent spécifier une requête que seul ce gestionnaire comprend en général. Quoi qu'il en soit,
les requêtes de ce type utilisent elles aussi un descripteur de fichier spécial de périphérique,
ce qui fait que tous les accès au matériel ont lieu par l'intermédiaire de ces fichiers.
Il existe deux principaux types de fichiers spéciaux de périphériques. Le premier type correspond aux périphériques de type bloc, dont les données ne peuvent être lues que par blocs (c'est le cas des disques durs, des lecteurs de CD et de disquettes en particulier). Le deuxième type correspond aux périphériques de type caractère, dont les données peuvent être lues caractère par caractère (cartes son, ports série, etc.).
En plus de son type, chaque fichier spécial de périphérique est caractérisé par deux codes permettant d'identifier le type et le modèle du périphérique auquel il donne accès. Ces deux codes portent le nom de code majeur et de code mineur. C'est par l'intermédiaire de ces codes que le noyau est capable de retrouver le gestionnaire de périphériques à utiliser pour satisfaire aux requêtes des clients qui accèdent à un fichier spécial de périphérique. Il y a donc, en général, une association unique entre ces codes et les gestionnaires de périphériques.
Les fichiers spéciaux de périphériques sont tous stockés dans le répertoire /dev/. Selon votre distribution, ce répertoire peut être géré de manière totalement dynamique en fonction du matériel effectivement installé sur votre machine, ou être prérempli statiquement avec les fichiers spéciaux de la plupart des périphériques rencontrés sur le marché (y compris donc pour des périphériques inexistants sur votre machine). Nous allons voir dans les sections suivantes comment ces fichiers sont créés et utilisés par le système en relation avec les pilotes de périphériques.
Depuis la version 2.6 du noyau, Linux dispose d'une fonctionnalité permettant aux applications de détecter l'apparition et la suppression des périphériques, que ceux-ci soient détectés au démarrage du système ou qu'ils soient branchés à chaud une fois l'ordinateur allumé.
Cette fonctionnalité est principalement utilisée par le sous-système udev (abréviation de « Userspace /dev »). Comme son nom l'indique, udev a pour principale fonction de prendre en charge la gestion du répertoire /dev/, mais en réalité il est capable de faire beaucoup mieux que cela.
En particulier, udev peut réaliser les opérations suivantes lorsque le noyau signale la présence d'un nouveau périphérique :
chargement du module du pilote de périphérique si nécessaire ;
si le périphérique requiert un firmware, chargement de celui-ci ;
création du fichier spécial de périphérique et de ses alias nécessaires à l'utilisation du périphérique ;
exécution des opérations d'initialisation complémentaires ou lancement des applications utilitaires associées au périphérique ;
notification de la présence du périphérique à l'ensemble des autres programmes qui s'intéressent à la gestion du matériel (par exemple le gestionnaire de bureau).
udev peut également effectuer les opérations nécessaires à la suppression d'un périphérique, ce qui est utile pour les périphériques amovibles. Ainsi, la détection, la configuration et la notification dynamique de la présence des périphériques Plug and Play est totalement automatisée.
Note : Le noyau, et donc udev, ne sont en réalité capables de détecter que les périphériques connectés au bus IDE, USB, PCMCIA, FireWire ou PCI. En effet, ces bus utilisent des technologies suffisamment récentes et permettent la détection, la configuration automatique et l'identification précise des périphériques. De ce fait, seuls les pilotes de périphériques de ce type peuvent être chargés automatiquement par udev. Les pilotes des périphériques plus anciens (périphériques ISA par exemple) ne peuvent être chargés automatiquement, car ces périphériques ne sont détectés que si leur pilote est déjà chargé dans le noyau. Aussi est-il nécessaire de précharger ces pilotes au démarrage de l'ordinateur, ou de les intégrer directement dans le noyau, pour qu'ils soient reconnus automatiquement par le système.
udev permet de résoudre plusieurs problèmes concernant les fichiers spéciaux de périphériques. Le plus évident pour l'utilisateur est que seuls les fichiers spéciaux des périphériques effectivement présents et pris en charge par un gestionnaire de périphérique apparaissent dans le répertoire /dev/.
Le répertoire /dev/ n'a donc plus à contenir les fichiers spéciaux de périphériques pour tous les périphériques pris en charge par Linux. Il en est d'autant réduit, ce qui permet de le placer dans un système de fichiers virtuel (c'est-à-dire un système de fichiers géré par le noyau en mémoire). La plupart des distributions basées sur udev utilisent cette technique afin d'accélérer la phase de chargement du système et les accès aux fichiers spéciaux de périphériques par les applications, et d'éviter de consommer inutilement de l'espace disque pour stocker des fichiers spéciaux qui sont de toutes manières recréés dynamiquement à chaque démarrage.
Un autre avantage d'udev est que les codes majeurs et mineurs des fichiers spéciaux de périphériques peuvent à présent être attribuées dynamiquement par le noyau lorsque les gestionnaires de périphériques s'initialisent. Cela a plusieurs conséquences :
il n'y a plus besoin d'avoir recours à une autorité centrale pour obtenir les valeurs de codes mineurs et majeurs, ceux-ci pouvant être déterminés dynamiquement par le noyau ;
il n'y a plus de risque de conflit entre deux périphériques, le noyau s'assurant de l'unicité des codes utilisés ;
la limitation du nombre de périphériques due au nombre limité de codes majeurs et mineurs n'existe plus (il suffit de consulter la liste du fichier /usr/src/linux/Documentation/devices.txt pour constater qu'il reste peu de codes libres pour les périphériques à venir).
Enfin, udev permet de simplifier considérablement la configuration et l'utilisation du système. Par exemple, il est possible de fixer de manière permanente le nom des fichiers spéciaux des périphériques amovibles, et les environnements utilisateurs graphiques peuvent réagir à l'apparition et à la suppression des périphériques afin de permettre leur utilisation par l'utilisateur.
Le principe de fonctionnement de udev est le suivant. Lorsque le noyau détecte un changement dans la configuration matérielle, il signale ce changement via un canal de communication « NetLink » aux applications qui s'y intéressent. udev utilise le démon udevd pour écouter sur ce canal de communication, et il met chacun de ces événements dans une file pour les traiter dans leur ordre d'apparition.
Les événements envoyés par le noyau contiennent toutes les informations relatives au périphérique pour lequel le noyau a généré un événement. Ces paramètres comprennent, entre autres, la nature de l'événement (apparition ou disparition d'un périphérique inconnu sur un bus, demande de chargement de firmware, notification de chargement ou de déchargement d'un pilote de périphérique, etc.). udevd utilise ces informations, ainsi que les informations sur le périphérique fournies par le noyau au travers du système de fichiers /sys/, pour effectuer les opérations de chargement des modules de pilotes de périphérique, de chargement des firmware, et de création des fichiers spéciaux de périphériques.
Note : Le démon udevd exécute les commandes udev selon l'ordre indiqué par le noyau via un numéro de séquence. Cette sérialisation est nécessaire parce que le noyau est capable de générer plusieurs événements simultanément pour les périphériques complexes, et qu'il faut s'assurer que les pilotes de périphériques parents sont bien initialisés avant de charger les pilotes des sous-périphériques. Ce cas peut se produire par exemple pour les cartes d'extension ou pour les hub USB.
Les anciennes versions de udev n'écoutaient pas directement les événements générés par le noyau. Le mécanisme utilisé était légèrement plus complexe et passait par un programme utilitaire nommé hotplug. Le noyau appelait ce programme à chaque fois qu'un événement se produisait au niveau de la configuration matérielle. Ce programme déterminait les modules de pilotes de périphériques à charger, effectuait le chargement de ces modules, et initialisait les périphériques avec leur firmware si nécessaire. Puis, il envoyait les événements au démon udevd pour que celui-ci mette à jour le répertoire /dev/. Ce mécanisme a été abandonné au profit d'une gestion totalement intégrée dans udevd en raison des mauvaises performances dues à l'exécution de l'utilitaire hotplug pour chaque événement matériel.
Le comportement que udev doit adopter dépend bien évidemment de chacun des périphériques, ou du moins de la classe du périphérique pour lequel l'événement se produit. Ce comportement est donc paramétrable à l'aide des fichiers de configuration situés dans le répertoire /etc/udev/ et /etc/udev/rules.d/. Ce dernier répertoire contient en particulier les règles qui permettent de définir les fichiers spéciaux de périphériques qui doivent être créés, les alias permettant de les nommer de manière constante, et les permissions que l'on doit leur affecter.
Le répertoire
/etc/udev/ ne contient généralement que le fichier de configuration
principal d'udev, à savoir le fichier udev.conf. Ce fichier permet
d'indiquer le répertoire cible dans lequel les fichiers spéciaux de périphérique seront créés
(paramètre udev_root
, dont la valeur doit obligatoirement être
/dev/), le répertoire contenant les fichiers de définition
des actions prises par udev en fonction des différents événements (paramètre
udev_rules
, dont la valeur est normalement
/etc/udev/rules.d/), et le niveau d'erreur à partir
duquel les traces d'udev seront enregistrés dans le journal du système syslog
(paramètre udev_log
).
L'essentiel de la configuration réside toutefois dans les fichiers du répertoire /etc/udev/rules.d/. Ces fichiers expriment, sous la forme de règles permettant de sélectionner les différents événements provenant du noyau en fonction des informations de cet événement, les actions à entreprendre. Ce mécanisme est suffisamment générique pour permettre d'identifier de manière unique les périphériques et de réaliser les actions dont udev a la charge.
Les règles sont décrites à raison d'une ligne par règle dans les fichiers de configuration. L'action identifiée par chaque règle n'est exécutée que si l'événement généré par le noyau correspond aux critères de sélection de la règle. Certains mots clés permettent d'obtenir les informations sur l'événement produit par le noyau, et d'autres mots clés permettent de décrire les actions à effectuer lorsque les critères de sélection de l'événement sont satisfaits.
Par exemple, le mot clé ACTION identifie la nature de l'événement. Le mot clé KERNEL donne le nom que le noyau donne au périphérique qui a généré l'événement, et DEVPATH le chemin du périphérique dans le système de fichiers virtuel /sys/. Inversement, le mot clé NAME permet de paramétrer l'action de création du fichier spécial de périphérique en définissant le nom de ce fichier, et OWNER, GROUP et MODE permettent de définir respectivement l'utilisateur propriétaire, le groupe, et les droits d'accès à ce fichier. Ainsi, les simples règles suivantes :
KERNEL=="mem", NAME="%k", GROUP="kmem", MODE="0640" KERNEL=="kmem", NAME="%k", GROUP="kmem", MODE="0640"
Note : Notez bien que dans les définitions de règle, le signe '==' est utilisé pour indiquer une contrainte d'égalité, alors que le signe '=' est utilisé pour réaliser une affectation, et constitue donc une partie de l'action de la règle.
D'autres mots clés sont disponibles. Ainsi, SYMLINK permet de définir des liens symboliques supplémentaires (par exemple /dev/modem ou /dev/mouse) référençant les fichiers spéciaux de périphériques réels. Le mot clé PROGRAM permet d'exécuter un programme avant que le fichier spécial de périphérique ne soit créé (généralement pour déterminer le nom du fichier à créer, justement), et le mot clé RUN permet d'exécuter une commande après que le fichier spécial de périphérique a été créé (généralement pour configurer le périphérique ou signaler sa présence à l'utilisateur). Vous trouverez de plus amples renseignements sur toutes ces règles dans la page de manuel de udev, ainsi que dans la documentation fournie avec udev pour la rédaction de règles personalisées.
Les fichiers du répertoire /etc/udev/rules.d/ doivent tous avoir l'extension .rules. De plus, ces fichiers sont interprétés dans l'ordre lexicographique de leur nom. Ainsi, il est d'usage de préfixer le nom du fichier par un numéro indiquant l'ordre de priorité des règles de ce fichier par rapport aux autres. Par exemple, les règles du fichier 50-udev.rules (fichier de définition des règles principales de udev) sont toujours lues avant les règles du fichier 90-hal.rules (fichier de définition des règles permettant d'envoyer les événements concernant le matériel au démon d'abstraction du matériel hald), car 50 est plus petit que 90 dans l'ordre lexicographique.
Comme vous pouvez le constater, les fichiers de configuration de udev permettent de paramétrer de manière très souple et très précise les actions à effectuer lorsqu'un périphérique apparaît ou disparaît. Nous verrons dans les sections suivantes comment les principales actions sont réalisées. La contrepartie de ce système est tout de même une certaine complexité dans la définition des règles des fichiers de configuration d'udev, mais vous n'aurez généralement pas à vous en préoccuper, car les distributions fournissent des fichiers de configuration adaptés à la plupart des usages.
Généralement, les périphériques modernes disposent d'identifiants qui leur sont propres et qui peuvent être récupérés par des mécanismes standards des bus modernes. Ces identifiants sont généralement au nombre de deux, le premier étant un code spécifique au fabricant du périphérique, et le deuxième étant un identifiant de ce périphérique. Par exemple, vous pouvez visualiser les différents périphériques connectés au bus PCI de votre ordinateur avec la simple commande lspci -nn. La commande lsusb vous donnera des identifiants similaires pour les périphériques USB.
udev peut déterminer, à l'aide des identifiants du matériel, déterminer quels sont les pilotes de périphériques capable de gérer ce matériel. En effet, les fichiers sources de chaque module de pilote de périphérique contiennent une liste des identifiants des périphériques qu'ils prennent en charge. Lors de l'installation du module, la command depmod extrait ces informations et les place dans un des fichiers situés dans le répertoire d'installation des modules du noyau (à savoir /lib/module/version/, où version est le numéro de version du noyau). depmod crée un fichier pour chaque type de périphérique. Ces fichiers ont tous un nom de la forme :
où type est le type de périphérique.Par exemple, le fichier module.usbmap contient, pour chaque périphérique USB pris en charge par les modules installés, une ligne indiquant le nom du module et l'ensemble des identifiants du périphérique USB en question.
Ainsi, lorsque le noyau indique à udev l'apparition d'un nouveau périphérique, celui-ci consulte les fichiers d'association des modules aux périphériques pour déterminer quel module est capable de gérer ce périphérique. Ce module est chargé si nécessaire, et le gestionnaire de périphérique peut s'initialiser.
Note : La table des périphériques pris en charge par certains modules peut ne pas être complète ou à jour. C'est généralement le cas pour les nouveaux périphériques vendus par un fabricant qui utilisent une nouvelle révision d'une puce électronique que des anciens périphériques supportés par un pilote utilisait déjà. Ainsi, lorsqu'un périphérique n'est pas reconnu immédiatement par les pilotes existants, vous pouvez tenter une recherche sur Internet avec les identifiants de périphériques tels qu'ils sont donnés par lspci ou lsusb et identifier le pilote nécessaire pour ce périphérique. S'il en existe un, vous pouvez essayer d'ajouter les identifiants de votre périphérique dans les sources du pilote, le recompiler et l'installer à nouveau. Si ce pilote vous permet d'utiliser effectivement ce périphérique de manière stable, vous pouvez alors demander au mainteneur du pilote d'ajouter les identifiants du périphérique dans la liste des périphériques supportés.
Certains périphériques nécessitent un firmware (c'est-à-dire un micro logiciel pour les périphériques évolués) pour fonctionner correctement. Ce firmware peut être stocké de manière permanente dans le périphérique dans une mémoire non volatile, ou devoir être rechargé à chaque initialisation du périphérique. Dans ce cas, cette opération de chargement doit être faite par le pilote de périphérique.
Grâce à udev, il est
possible de faire en sorte que cette opération se fasse automatiquement. Lorsqu'un pilote
désire charger le firmware dans le périphérique, il appelle une fonction du noyau qui
génère un événement udev. udev prend alors en charge les opérations et localise le firmware,
puis le fournit au noyau. Le pilote de périphérique peut alors effectuer le chargement
effectif du firmware dans le périphérique. Pour cela, udev copie le fichier de firmware
demandé par le pilote de périphérique dans un fichier dédié à cet effet dans l'entrée
du périphérique du système de fichiers virtuel /sys/.
Bien entendu, le chemin permettant de trouver les informations du périphérique dans le système
de fichiers virtuel /sys/ est fourni aux scripts
de configuration d'udev via le mot clé DEVPATH
.
En général, les fichiers de firmware sont fournis avec les pilotes de périphériques ou sont disponibles sur le site Web indiqué dans leur documentation. Ils doivent être installés dans le répertoire /lib/firmware/. Il est également nécessaire d''activer l'option « Hotplug firmware loading support » du sous-menu « Generic Driver Options » du menu « Device Drivers » pour que cette fonctionnalité soit disponible.
À chaque périphérique détecté par un pilote de périphérique, le noyau génère un événement à destination d'udev. Ces événements indiquent le nom du périphérique tel qu'il est connu par le gestionnaire de périphérique, ainsi que les informations sur le périphérique dans le système de fichiers /sys/. udev utilise ces informations pour déterminer les règles qui indiquent quels sont les fichiers spéciaux de périphériques à utiliser.
Si aucune règle ne correspond, udev crée le fichier spécial de périphérique avec le nom utilisé par le pilote de périphérique pour l'identifier. Dans le cas contraire, c'est le nom fourni dans les fichiers de configuration qui est utilisé. De même, les fichiers sont créés par défaut au nom de l'utilisateur root, et seul cet utilisateur dispose des droits de lecture et d'écriture sur ces fichiers. Enfin, si des liens symboliques sont définis dans les règles appliquées, ceux-ci sont créés. Par exemple, il est classique d'avoir un lien symbolique cdrom pointant vers le périphérique de type bloc gérant le lecteur de CD.
Les règles d'udev sont généralement fournies par les distributions et permettent de créer les fichiers spéciaux de périphériques de manière générique, ainsi que les liens symboliques standards. De même, bien que les interfaces réseau ne sont pas accédées par l'intermédiaire de fichiers spéciaux de périphériques, les règles udev sont également appliquées afin de les renommer sous un nom spécifique.
L'un des princpaux avantages d'udev est de permettre un nommage fixe des différents périphériques. Par exemple, si vous disposez de deux imprimantes, chaque imprimante se verra affecter un fichier spécial de périphérique dédié. Toutefois, il est possible qu'une de ces imprimantes ne soit pas disponible, ou pas allumée. Dans ce cas, un décallage des fichiers spéciaux de périphériques peut se produire, ce qui peut être génant si une configuration spécifique doit être faite pour la deuxième imprimante. De même, vous pouvez disposer de plusieurs disques amovibles ou clefs USB, et il peut être utile que les fichiers spéciaux de ces périphériques aient un nom constant.
udev permet tout cela, en faisant en sorte que les règles des fichiers de configuration sélectionnent les périphériques sur une de leur caractéristiques principale (par exemple, le nom de modèle du périphérique).
Pour fixer les idées et donner un exemple, supposons que l'on ait deux clefs mémoire USB de modèles différents et que l'on désire affecter de manière permanente des fichiers de périphériques à chacune d'elle. Il suffit pour cela simplement de trouver un paramètre unique dans les identifiants de ces périphériques afin de les distinguer.
La manière la plus simple pour obtenir la liste
des paramètres d'un périphérique est d'utiliser la commande udevinfo.
Pour obtenir la liste des attributs utilisables dans les règles de udev,
il faut utiliser l'option --attribute-walk
. Le périphérique dont les attributs
doivent être récupérés peut être spécifié soit grâce au nom de son fichier spécial de périphérique
utilisé par le noyau (avec l'option --name
), ou avec son chemin dans le système
de fichiers /sys/.
Note : La commande udevmonitor peut vous permettre de déterminer le chemin du périphérique dans le système de fichiers /sys/.
Par exemple, la commande suivante permet d'obtenir les informations sur un disque amovible connecté sur le port USB et apparaissant sous le nom /dev/sdb :
Supposons que cette commande donne le résultat suivant :Udevinfo starts with the device specified by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to match, can be composed by the attributes of the device and the attributes from one single parent device. looking at device '/block/sdb': KERNEL=="sdb" SUBSYSTEM=="block" DRIVER=="" ATTR{capability}=="12" ATTR{stat}==" 52 127 556 486 0 0 0 0 0 426 486" ATTR{size}=="156301488" ATTR{removable}=="0" ATTR{range}=="16" ATTR{dev}=="8:16" looking at parent device '/devices/pci0000:00/0000:00:02.2/usb1/1-2/1-2:1.0/host5/target5:0:0/5:0:0:0': KERNELS=="5:0:0:0" SUBSYSTEMS=="scsi" DRIVERS=="sd" ATTRS{modalias}=="scsi:t-0x00" ATTRS{ioerr_cnt}=="0x0" ATTRS{iodone_cnt}=="0x3b" ATTRS{iorequest_cnt}=="0x3b" ATTRS{iocounterbits}=="32" ATTRS{timeout}=="30" ATTRS{state}=="running" ATTRS{rev}=="09.0" ATTRS{model}=="WD800VE-00HDT0 " ATTRS{vendor}=="WDC " ATTRS{scsi_level}=="3" ATTRS{type}=="0" ATTRS{queue_type}=="none" ATTRS{queue_depth}=="1" ATTRS{device_blocked}=="0" ATTRS{max_sectors}=="240" ...
Comme on peut le voir, les informations du périphérique /dev/sdb sont d'abord affichées. Viennent ensuite les informations des périphériques parents, donc en particulier les informations sur le disque physique (ici, un Western Digital WD800VE-00HDT0), et ainsi de suite (seules les deux premiers bloc d'information ont été recopiés dans cet exemple, les suivants identifiant l'adaptateur USB et les composants du bus USB eux-mêmes).
Ainsi, les informations spécifique au matériel connecté que l'on doit utiliser pour définir un fichier spécial de périphérique unique sont données dans le deuxième groupe d'information (relative au disque physique). Il est donc possible de définir la règle udev suivante pour créer un fichier spécial de périphérique /dev/hd_externe dédié à ce disque :
Notez cependant que l'on ne peut pas, dans une même règle udev mélanger des critères de sélection utilisant des informations des différents groupes d'informations renvoyés par udevinfo, parce que chaque groupe identifie un périphérique logique et un seul dans le noyau.Une fois vos règles définies, vous pouvez les tester avec la commande udevtest. Cette commande prend en paramètre le nom du périphérique de plus haut niveau renvoyé par udevinfo. Dans notre exemple, il s'agit de /block/sdb :
Cette commande affichera l'ensemble des opérations que udev effectuerait si ce périphérique était connecté. Attention toutefois, les informations affichées proviennent d'une simulation, et peuvent être incorrectes si des règles udev utilisent des commandes externes, puisque en simulation ces commandes ne sont pas exécutés.En pratique, les distributions font en sorte que les règles de nommage des périphériques soient écrites ou mises à jour dynamiquement lors de la première détection du périphérique. Elles s'appuient pour cela sur des programmes utilitaires, généralement situés dans le répertoire /lib/udev/. Par exemple, le script /lib/udev/cdrom-symlinks.sh a pour but de créer le fichier de configuration /etc/udev/rules.d/75-optical-devices.rules, qui contient les définitions des liens symboliques sur les lecteurs de disque optique (CDROM et graveurs de CD par exemple).
Note : Les scripts de génération de règles peuvent ne pas nommer les périphériques comme vous le désirez (par exemple, le fichier spécial /dev/cdrom peut référencer votre graveur de CD, alors que le fichier spécial /dev/cdwriter un simple lecteur de CD. Dans ce cas, vous pourrez modifier directement le fichier de configuration 75-optical-devices.rules pour remettre les choses dans l'ordre. Si vous vous trompez, supprimez simplement ce fichier, il sera recréé automatiquement au prochain redémarrage d'udev.
udev est également livré avec un fichier de configuration contenant une règle permettant de notifier les applications de haut niveau des apparitions et disparition des périphériques.
Le mécanisme utilisé se base ici sur le sous-système HAL (« Hardware Abstraction Layer Daemon »). Ce sous-système a pour principal but de fournir une interface unifiée pour que l'ensemble des applications de haut niveau (par exemple les gestionnaires de bureau KDE et Gnome) puisse obtenir des informations sur les périphériques et être notifiées de toutes modification de la configuration matérielle.
Toutes ces opérations sont implémentées au niveau du démon hald. Ce démon reçoit les informations concernant les périphériques directement de udev, grâce à la règle mentionnée ci-dessus. Grâce à ces informations, il maintient une base de données des périphériques présents et de leurs principales caractéristiques.
De plus, hald fournit une interface de programmation standard pour toutes les applications utilisateurs. Cette interface se base sur un protocole de communication standard, le protocole D-BUS. Ce protocole a été développé de manière commune avec les deux principaux environnements de bureau de Linux, à savoir KDE et Gnome. Ainsi, les applications de ces environnements sont capables de gérer les modificiations intervenant sur le matériel. Par exemple, le fait de brancher une clef USB ou un disque externe permet de demander à l'utilisateur s'il veut le monter ou non.
Note : Le protocole de communication DBUS a été conçu de manière générique est n'a pas pour vocation de n'être utilisé que par HAL. En réalité, c'est tout un système de communication inter processus de haut niveau et d'utilisation simplifiée (par rapport aux systèmes de composants lourds tels que CORBA par exemple).
DBUS permet les communications entre les applications utilisateurs d'un même utilisateur, ainsi que les communications transverses entre les applications système. En pratique, ces communications sont gérées par un démon, dont une instance est lancée pour le système, et potentiellement une instance supplémentaire est exécutée pour chaque utilisateur. Les mécanismes de DBUS et de HAL ne sont d'intérêt que pour les programmeurs et ne seront donc pas détaillés plus dans ce document.
Pour terminer cette description des mécanismes d'udev, signalons que le démon udevd ne peut évidemment pas écouter les événements du noyau avant qu'il ne soit lancé. Par conséquent, les périphériques détectés par le noyau avant le démarrage du démon udevd ne sont pas être détéctés automatiquement.
Ce problème est géré au niveau des scripts de démarrage des distributions, en rejouant les événements matériels des périphériques déjà détectés par le noyau. Cela se fait en appelant la commande udevtrigger. Cette opération est totalement prise en charge par les distributions, et vous n'avez normalement pas à utiliser cette commande.
La gestion des périphériques et du répertoire /dev/ par udev est utilisée par la plupart des distributions récentes. Toutefois, les anciennes distributions sont encore susceptibles d'utiliser un répertoire /dev/ complètement statique, c'est-à-dire rempli à l'avance avec les fichiers spéciaux des périphériques les plus courants.
Normalement, votre distribution fournit un répertoire /dev/ relativement complet. Vous n'avez donc pas à toucher au répertoire /dev/. Cependant, si vous devez créer un fichier spécial de périphérique vous-même, par exemple pour un gestionnaire de périphérique exotique non pris en charge par votre distribution ou non reconnu par le système, vous devrez utiliser la commande mknod. Sa syntaxe est relativement simple :
mknod fichier type majeur mineuroù fichier est le nom du fichier spécial de périphérique à créer, type est une lettre indiquant le type du fichier spécial ('c' pour les fichiers de type caractère et 'b' pour les fichiers de type bloc), et majeur et mineur sont les codes majeur et mineur du périphérique. Je vous invite à consulter la page de manuel de la commande mknod pour plus d'informations.
Note : La liste des codes majeurs et mineurs déjà affectés à des périphériques est donnée dans le fichier /usr/src/linux/Documentation/devices.txt. Comme vous pouvez le constater, l'utilisation d'un répertoire /dev/ statique impose donc de disposer d'une autorité centrale régissant l'affectation des codes majeurs et mineurs aux gestionnaire de périphériques. De plus, la plupart des codes sont déjà affectés, ce qui est une des raisons pour laquelle le système udev a été développé.
Lorsqu'une application tente d'accéder à un fichier spécial de périphérique, trois cas peuvent se présenter :
soit un pilote de périphérique capable de gérer le périphérique est déjà chargé ;
soit il n'existe aucun pilote capable de gérer le périphérique, mais le noyau peut en trouver un ;
soit le noyau est incapable de trouver un pilote pour ce périphérique.
Dans les deux premiers cas, la requête de l'application est satisfaite, éventuellement après le chargement automatique du pilote de périphérique sous la forme de module. Dans le troisième cas, évidemment, la requête est refusée.
Ainsi, lors de l'ouverture d'un fichier spécial de périphérique par une application, le noyau vérifie s'il dispose d'un pilote de périphérique capable de gérer ce périphérique, en s'appuyant sur les codes majeur et mineur du fichier spécial de périphérique. Si ce n'est pas le cas, il effectue un appel à modprobe pour charger le module contenant ce pilote de périphérique.
Le nom du module dont le noyau demande le chargement à modprobe dépend du pilote et ne correspond pas forcément à un nom de module existant. Par exemple, le module de gestion des ports parallèles se nomme, sur les ordinateurs de type PC, parport_pc. Toutefois, lorsque le noyau désire accéder au port parallèle, il n'utilise pas ce nom, mais plutôt un nom générique, qui est le même pour toutes les architectures matérielles supportées par Linux. Ce nom est parport_lowlevel. Ainsi, le noyau utilise la commande suivante pour charger le module de prise en charge du port parallèle :
L'option-k
permet d'indiquer à modprobe qu'il est appelé
par le noyau, donc dans le contexte de chargement automatique des modules.
modprobe doit donc faire la correspondance entre le nom de module demandé par le noyau et le nom d'un module réel. C'est donc dans les fichiers de configuration de modprobe que cette correspondance est définie. Pour cela, un certain nombre d'alias peuvent être définis pour identifier les modules réels. Chaque alias est introduit par le mot clé alias, dont la syntaxe est donnée ci-dessous :
alias nom moduleoù nom est le nom de l'alias, et module est le nom du module réel. Lorsque la commande modprobe est appelée avec le nom d'un alias en paramètre, elle commence par rechercher le nom du module réel dans ses fichiers de configuration, puis elle le charge en mémoire.
Par exemple, pour charger le module parport_pc automatiquement lorsque le noyau a besoin d'accéder au port parallèle, on pourra ajouter la définition d'alias suivante dans le fichier modprobe.conf :
Vous pouvez constater que le mécanisme d'alias permet de rendre le noyau indépendant des modules utilisés, puisque l'association entre le nom du module utilisé par le noyau et le module réel et ses paramètres est maintenu dans des fichiers de configuration. L'inconvénient de cette méthode est en revanche qu'il faut connaître les noms de modules utilisés par le noyau pour chaque pilote et pour leurs fonctionnalités. Ces noms sont assez variables et dépendent de la fonctionnalité demandée. En général, ce nom apparaît dans les messages de traces du noyau lorsqu'il ne parvient pas à localiser un module lors de son chargement automatique. Ces messages de traces peuvent être affichés à l'aide de la commande dmesg. Il est donc possible de déterminer la liste des modules que le noyau a tenté de charger relativement facilement. Toutefois, cela n'indique pas forcément la fonctionnalité implémentée par le module en question, et encore moins l'application qui a tenté d'utiliser cette fonctionnalité.
Il est heureusement beaucoup plus facile de savoir quelle est la fonctionnalité demandée lorsque celle-ci a trait à un périphérique de l'ordinateur. En effet, le nom de module utilisé par le noyau pour chaque gestionnaire de périphérique est construit à partir du type de fichier spécial de périphérique et de ses codes majeur et mineur. Ainsi, le noyau utilise un nom de module de la forme « char-major-xxxx » pour les périphériques de type caractère, et un nom de la forme « block-major-xxxx » pour les périphériques de type bloc. Les caractères xxxx identifient le code majeur de ce périphérique, plus rarement son code mineur. Il est donc facile de déduire le gestionnaire de périphérique nécessaire à la gestion de ce fichier spécial de périphérique, et de définir l'alias permettant de faire la correspondance entre le nom du module utilisé par le noyau et le module réel à charger.
Par exemple, pour les cartes son, le nom de module char-major-14 est utilisé pour demander le chargement du module de gestion du son, parce que les cartes son utilisent toutes le code majeur 14. Il faut donc définir un alias sur ce nom de module vers le module du gestionnaire de cartes son. Comme on le verra dans la section traitant de la configuration des cartes son, ce module pourra lui-même demander le chargement de modules complémentaires, en fonction de la carte son réellement utilisée et des fonctionnalités demandées. Le numéro de code mineur sera indiqué lors de ces opérations.
Si vous devez modifier les fichiers de configuration de modprobe, sachez qu'il s'agit d'une opération délicate, car elle nécessite de bien connaître les mécanismes de nommage utilisés par le noyau, les noms des modules réels et leurs paramètres de configuration. En général, vous trouverez les informations sur les entrées à ajouter ou à modifier dans le fichier d'aide du module correspondant, que vous pourrez trouver dans les sources du noyau (dans le répertoire /usr/src/linux/Documentation/). Quoi qu'il en soit, la modification des fichiers de configuration de modprobe peut générer de nouvelles dépendances entre les modules. Il est donc nécessaire d'exécuter la commande depmod -a après chaque modification de ce fichier.