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 tous les 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 fichiers spéciaux de périphériques, les modules du noyau 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.
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. Comme nous le verrons plus tard, 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. 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 (ce fichier vous donnera donc une idée des périphériques qui peuvent être ou qui seront gérés par Linux).
Les fichiers spéciaux de périphériques sont tous stockés dans le répertoire /dev/. En général, ce répertoire contient un grand nombre de fichiers spéciaux de périphériques, même pour des périphériques inexistants sur votre machine. Ces fichiers sont généralement installés automatiquement par les distributions, qui laissent au noyau le soin de signaler que le périphérique est absent lorsqu'un programme tente d'accéder à un fichier spécial auquel aucun matériel ne correspond. Les fichiers fournis par les distributions sont donc, en théorie, susceptibles de couvrir toutes les gammes de périphériques courants, et vous n'aurez pas à toucher au répertoire /dev/. Cependant, si vous devez créer un fichier spécial de périphérique vous-mê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.
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 :
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. Toutes ces options sont définies dans le fichier de configuration/etc/modprobe.conf. Lors du chargement d'un module, modprobe consulte ce fichier et passe les paramètres indiqués au module à charger.
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 plus facile de les enregistrer dans le fichier /etc/modprobe.conf. Cela se fait sur une ligne commençant par le mot clé options, 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.
Le fichier modprobe.conf permet é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.Il n'est normalement pas nécessaire d'utiliser les commandes insmod ou modprobe pour charger les modules. En effet, il est possible de faire en sorte que les modules soient chargés à la demande, lorsqu'une de leurs fonctionnalités est demandée au niveau du noyau. Pour cela, le noyau utilise lui-même la commande modprobe pour charger les modules, ce qui assure la gestion correcte des dépendances entre les modules. À chaque fois que le noyau a besoin d'une fonctionnalité qui n'est pas encore chargée, il effectue une requête à modprobe en lui demandant de charger le module manquant.
Le nom du module dont le noyau demande le chargement à modprobe dépend évidemment de la fonctionnalité demandée 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 encore une fois dans le fichier de configuration /etc/modprobe.conf que cette correspondance est enregistrée. 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 le fichier modprobe.conf, 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 devra 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 le fichier de configuration modprobe.conf. 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 fonctionnalité. 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 des informations définissant ce fichier, ce qui permet de l'identifier et donc de trouver le module à charger pour prendre en charge ce périphérique. En général, 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. Ainsi, le périphérique accédé par le noyau est parfaitement identifié tant du point de vue de sa nature (disque, périphérique de type caractère...) que du point de vue de ses codes majeur et mineur. Il est donc facile d'en 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 le fichiermodprobe.conf, 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 de modprobe.conf 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.
Pour les périphériques intégrés directement dans le noyau, il n'est pas possible d'utiliser le fichier modprobe.conf pour définir les différentes options dont ils peuvent avoir besoin. Dans ce cas, il faut fournir ces options au démarrage du système.
Cela se fait, comme nous l'avons vu dans la Section 3.4.7,
en fournissant des paramètres au noyau 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.
Généralement, les options du noyau sont fournies directement dans la ligne de commande du noyau par le gestionnaire d'amorçage. Ces options sont ensuite 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, via les systèmes de fichiers virtuels /proc/ ou /sys/.
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.
D'une manière générale, les fonctionnalités développées récemment et utilisable soit en tant que module, soit en tant que fonctionnalité intégrée directement dans le noyau, utilisent une syntaxe générique permettant de fixer leurs options. Cette syntaxe est la suivante :
module.paramètre=valeuroù module est le nom du module du noyau qui implémente cette 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 le fichier /etc/modprobe.conf. 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. En effet, il est souvent nécessaire de redémarrer pour prendre en compte de nouveaux paramètres dans ce cas, alors qu'avec les modules du noyau, un déchargement et un rechargement suffit. Par contre, lorsque votre configuration sera finie, vous pourrez parfaitement vous passez des modules et désactiver les fonctions de détection automatique du matériel utilisées par la plupart des distributions : vous gagnerez ainsi plusieurs dizaines de secondes au démarrage de votre ordinateur.
Dans certaines situations, l'usage des modules reste impératif. C'est par exemple le cas lorsque plusieurs périphériques identiques sont installés dans l'ordinateur, et que le gestionnaire de périphérique impose d'être chargé plusieurs fois. Dans ce cas, il faut le charger sous forme de module, avec, pour chaque périphérique, un nom de module différent. Cela est évidemment infaisable si l'on intègre le gestionnaire de périphérique directement dans le noyau.
Linux est capable de gérer les périphériques connectables à chaud, c'est-à-dire ordinateur allumé, de manière très souple, par l'intermédiaire de programmes auxiliaires. Plus précisément, le noyau se contente de détecter l'apparition et la disparition des périphériques USB, pcmcia, FireWire, PCI ou autres sur les bus systèmes, de récupérer les informations sur ces périphériques, et de les fournir à un programme auxiliaire chargé de faire tout le travail de configuration. Ce mécanisme permet de rendre le noyau absolument indépendant des différentes opérations éventuellement nécessaires pour configurer le matériel, et reste très souple et extensible pour les futurs périphériques connectables à chaud.
Dès qu'un événement de type apparition ou disparition d'un périphérique sur un bus se produit, le noyau appelle le programme référencé dans le fichier hotplug du répertoire /proc/sys/kernel/ et fournit à ce programme toutes les informations nécessaires à la configuration du périphérique : le type de périphérique qui vient d'apparaître ou de disparaître, ainsi que la description complète de ce périphérique.
Par défaut, le programme appelé par le noyau est le script /sbin/hotplug. Ce script identifie la nature du périphérique pour lequel le noyau signale un événement et délègue le travail de configuration à des sous-programmes, que l'on appelle « agents ». Ainsi, il existe un agent pour les périphériques USB, un agent pour les périphériques PCI, un agent pour les périphériques FireWire, etc. Tous ces agents sont normalement situés dans le répertoire /etc/hotplug/ et ont un nom de la forme
où type est le type du périphérique pour lequel le noyau appelle hotplug.Les agents ont besoin d'informations complémentaires pour déterminer quel est le périphérique qui vient d'apparaître ou de disparaître. Le noyau fournit ces informations à hotplug sous la forme de variables d'environnement spécifiques à chaque classe de périphérique. Par exemple, pour les périphériques USB, les variables d'environnement définies par le noyau sont les suivantes :
la variable ACTION, qui contient
la description de l'événement qui s'est produit pour ce périphérique ;
la variable PRODUCT, qui contient
les identificateurs du vendeur, du produit et du périphérique pour le périphérique considéré ;
la variable TYPE, qui contient
la classe et la sous-classe du périphérique, ainsi que son protocole de communication ;
et la variable INTERFACE,
qui contient les paramètres de classe, sous-classe et de protocole pour les interfaces, si le périphérique
USB est de classe 0.
Les informations fournies par le noyau à hotplug sont suffisantes pour que les agents puissent charger les gestionnaires de périphériques requis pour la prise en charge du matériel. Généralement, les événements hotplug se traduisent donc par le chargement d'un module du noyau. Afin de connaître le module qui contient le gestionnaire de périphérique, autrement dit le module à charger, hotplug doit pouvoir faire l'association entre les informations fournies par le noyau pour identifier le périphérique et les modules correspondants.
Cette association est maintenue par les modules eux-mêmes. En effet, chaque module de type gestionnaire de périphérique contient une table indiquant la liste des périphériques qu'il est capable de prendre 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 à hotplug 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 : Certains modules peuvent ne pas contenir de table de déclaration des périphériques qu'ils prennent en charge (cela relève du bogue). hotplug ne peut donc pas les charger automatiquement, car ces modules n'apparaissent pas dans les fichiers d'association du noyau. Il serait possible de rajouter explicitement les lignes manquantes dans ces fichiers, mais comme ils sont écrasés à chaque exécution de depmod, cette solution n'est pas pérenne. Par conséquent, hotplug utilise également des fichiers d'association complémentaires, que l'on peut modifier. Ces fichiers sont placés dans le répertoire /etc/hotplug/.
Depuis la version 2.6 du noyau, hotplug
est également capable de charger automatiquement le firmware des périphériques connectés.
Cette opération est réalisée par l'agent firmware.agent, en réponse
à un événement généré par le noyau lorsqu'un gestionnaire de périphérique demande le chargement
de son firmware. Cet agent reçoit en paramètre le nom du firmware à charger et le recherche
dans le répertoire /usr/lib/hotplug/firmware/. Une fois
le fichier du firmware localisé, l'agent le copie dans un fichier du système de fichiers
virtuel /sys/. Ce fichier est placé dans l'entrée
correspondant au périphérique dont le gestionnaire demande le firmware, et cette entrée
est également communiquée à hotplug par le noyau via la variable
d'environnement DEVPATH.
Normalement, le script hotplug et les agents utilisateurs doivent être fournis avec votre distribution. Vous n'aurez donc généralement pas à l'installer ni à vous préoccuper sur la manière dont les périphériques connectables à chaud doivent être configurés. En revanche, vous devrez activer la prise en charge des périphériques connectables à chaud au niveau du noyau. Pour cela, il faut simplement valider l'option « Support for hot-pluggable devices » du menu « General setup » dans la configuration du noyau. Cette option ajoute également l'entrée hotplug dans le répertoire /proc/sys/kernel/ du système de fichiers virtuels /proc/ du noyau.
Enfin, si vous désirez également utiliser les fonctionnalités de chargement de firmware du noyau, il vous suffit simplement de placer les fichiers de firmware dans le répertoire /usr/lib/hotplug/firmware/ et d'activer également l'option « Hotplug firmware loading support » du sous-menu « Generic Driver Options » du menu « Device Drivers ».
Depuis la version 2.6 du noyau, le mécanisme de détection des périphériques connectables à chaud peut également être utilisé pour définir automatiquement les fichiers spéciaux de périphériques du répertoire /dev/. Ce système s'appuie sur la commande udev (abréviation de « Userspace /dev »), qui permet de créer les fichiers spéciaux de périphériques dans le répertoire /dev/ à partir d'informations fournies par le noyau et des entrées définissant les périphériques dans le système de fichiers virtuel /sys/.
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/. Ce répertoire n'a donc plus à contenir les fichiers spéciaux de périphériques pour tous les périphériques pris en charge par Linux. Le répertoire /dev/ en est donc d'autant réduit et beaucoup plus lisible.
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 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. De plus, du fait du nombre plus réduit de fichiers spéciaux de périphériques, le répertoire /dev/ peut être un système de fichiers virtuel, monté sur un disque en mémoire.
Le principe de fonctionnement de udev est le suivant. Lorsque le noyau détecte un nouveau périphérique, il appelle, via les mécanismes de hotplug, la commande udevsend. Cette commande poste l'événement dans une file d'attente, gérée par le démon udevd. Ce démon se charge de la vider en appelant la commande udev pour réaliser les opérations de création ou de suppression des fichiers spéciaux de périphériques dans le répertoire /dev/.
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 hotplug simultanément, et que toutes les instances de hotplug s'exécutent de manière concurrente. Ces commandes pourraient donc ne pas s'exécuter dans le même ordre que celui demandé par le noyau sans cette précaution.
udevsend, et donc udev via le démon udevd, reçoit en paramètre, via un jeu de variables d'environnement, les informations relatives au périphérique pour lequel le noyau a généré un événement. Ces paramètres comprennent, entre autres, l'opération à effectuer (ajout ou suppression de fichiers spéciaux de périphériques) et le chemin sur le périphérique dans le système de fichiers /sys/. À partir de ces informations, udev peut consulter les fichiers de configuration du répertoire /etc/udev/ et déterminer quels sont les fichiers spéciaux de périphériques à créer, quels sont les droits que l'on doit leur affecter, et quels sont les liens symboliques à créer sur ces fichiers spéciaux. Ainsi, la définition des fichiers spéciaux de périphérique est-elle complètement paramétrable grâce aux fichiers de configuration de ce répertoire.
La politique de création des fichiers spéciaux de périphérique par udev est donc complètement décrite dans les fichiers de configuration du répertoire /etc/udev/. Le fichier de configuration principal est le fichier udev.conf. Ce fichier permet, entre autres, d'indiquer le répertoire cible dans lequel les fichiers spéciaux de périphériques seront créés (normalement /dev/), et les répertoires contenant les politiques de nommage et d'attribution des permissions aux fichiers spéciaux de périphériques (normalement /etc/udev/rules.d/ et /etc/udev/permissions.d/).
Les fichiers de définition de la politique de nommage des fichiers spéciaux de périphériques contiennent des lignes définissant chacune une règle de nommage. La ligne utilisée est choisie en fonction de critères de sélection basés sur les informations fournies par le noyau. Par exemple, il est possible de sélectionner le bus du périphérique (à l'aide du mot clé BUS), le nom donné au périphérique par le noyau (mot-clé KERNEL), ou encore l'identifiant du périphérique sur le bus (mot clé ID). Il est même possible d'exécuter un programme externe pour obtenir des informations complémentaires, et utiliser le résultat de cette commande comme critère de sélection (mot clé PROGRAM). Vous pouvez consulter la page de manuel de udev pour plus de détails sur la définition des critères de sélection des règles de nommage.
Outre les critères de sélection, les règles de nommage donnent bien entendu le nom du fichier spécial de périphérique à créer, son propriétaire et son groupe, ainsi que les permissions sur ce fichier, à l'aide des mots clefs NAME, OWNER, GROUP et MODE. Il est également possible de définir des liens symboliques complémentaires (par exemple /dev/cdrom ou /dev/modem) sur les fichiers spéciaux de périphériques ainsi créés (option SYMLINK). Les noms ainsi définis peuvent être paramétrés par les différentes informations sur le périphérique grâce à des variables spécifiques. La page de manuel de udev vous donnera encore une fois de plus amples informations à ce sujet.
Les permissions indiquées dans les fichiers du répertoire /etc/udev/permissions.d/ ne sont utilisées que si les règles de nommage ne donnent aucune permission. La syntaxe de ces fichiers est beaucoup plus simple, puisqu'elle associe, pour chaque nom de fichier spécial de périphérique, le propriétaire, le groupe et les droits d'accès (spécifiés en octal).
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 la manière dont les fichiers spéciaux de périphériques sont créés. La contrepartie est tout de même une certaine complexité dans la définition des règles de nommage et d'attribution des permissions, 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.
Les mécanismes de udev, pour complexes qu'ils puissent paraître, permettent de résoudre certains problèmes autrement insolubles. En particulier, udev permet de garantir que le nom de fichier spécial de périphérique affecté à un matériel donné ne change pas, quelle que soit la manière et l'ordre dont les périphériques sont connectés au système. Par exemple, il peut être intéressant que les fichiers spéciaux de périphériques attribués à deux imprimantes USB ne soient jamais échangés, ce qui pourrait se produire si on rajoute un hub USB et qu'on les y connecte a posteriori.
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 liste des paramètres des périphériques peut être affichée à l'aide de la commande systool.
Les disques USB apparaissant comme des périphériques SCSI.
Pour obtenir les informations sur ces périphériques, il faut utiliser, après les avoir connectés,
l'option -b de systool. On utilisera également l'option
-v, sans laquelle seul un résumé est affiché :
Bus = "scsi"
Device = "1:0:0:0"
Device path = "/sys/devices/pci0000:00/0000:00:02.0/usb2/2-2/2-2:1.0/host1/1:0:0:0"
delete = <store method only>
detach_state = "0"
device_blocked = "0"
max_sectors = "240"
model = "MINI DATA DRIVE "
queue_depth = "1"
rescan = <store method only>
rev = "1.00"
scsi_level = "3"
state = "running"
timeout = "30"
type = "0"
vendor = " "
Device = "2:0:0:0"
Device path = "/sys/devices/pci0000:00/0000:00:02.2/usb1/1-1/1-1:1.0/host2/2:0:0:0"
delete = <store method only>
detach_state = "0"
device_blocked = "0"
max_sectors = "240"
model = "USB DISK Pro "
queue_depth = "1"
rescan = <store method only>
rev = "1.08"
scsi_level = "3"
state = "running"
timeout = "30"
type = "0"
vendor = " "Supposons à présent que les deux clefs se distinguent par le nom de leur modèle, par exemple « USB DISK Pro » et « MINI DATA DRIVE ». Il est à présent possible d'affecter des fichiers spéciaux de périphériques de manière fixe à chacune de ces clefs, en ajoutant les règles suivantes dans le fichier udev.rules :
BUS="scsi", KERNEL="sd[a-z]", SYSFS_model="MINI DATA DRIVE*", NAME="usbda" BUS="scsi", KERNEL="sd[a-z][0-9]*", SYSFS_model="MINI DATA DRIVE*", NAME="usbda%n" BUS="scsi", KERNEL="sd[a-z]", SYSFS_model="USB DISK Pro*", NAME="usbdb" BUS="scsi", KERNEL="sd[a-z][0-9]*", SYSFS_model="USB DISK Pro*", NAME="usbdb%n"
Pour terminer cette description du mécanisme de création dynamique des fichiers spéciaux de périphériques, signalons qu'il est nécessaire de créer manuellement les fichiers spéciaux des périphériques des gestionnaires de périphériques intégrés au noyau lors de l'initialisation du système. En effet, les événements hotplug ne sont pas émis par le noyau dans ce cas. Ces gestionnaires doivent donc être recherchés dans le système de fichiers /sys/ et, pour chacun d'eux, udev doit être appelé explicitement. Ce travail est généralement réalisé par le programme udevstart, que les distributions exécutent au démarrage du système, après avoir mis en place l'éventuel système de fichier en mémoire /dev/ (et /après avoir monté le système de fichiers virtuel /sys).
Notez bien que udevstart ne permet de créer les fichiers spéciaux de périphériques que pour les périphériques dont les gestionnaires de périphériques sont chargés dans le noyau au moment de son exécution. De même, udev ne permet de créer les fichiers spéciaux de périphériques que pour les périphériques connectables à chaud. Par conséquent, aucun fichier spécial de périphérique ne sera créé pour les périphériques classiques dont le gestionnaire de périphérique n'est pas chargé avant l'exécution de udevstart. Comme aucune application ne peut accéder à ce fichier, le noyau ne chargera pas non plus le module du gestionnaire de périphérique, et le périphérique ne fonctionnera pas. Ce problème se rencontre par exemple pour le gestionnaire de périphérique du port parallèle et pour les vieux périphériques ISA. Dans ce cas de configuration, il est recommandé soit d'intégrer les gestionnaires de périphériques directement dans le noyau, soit de charger les modules correspondants dès le démarrage du système, avant l'exécution de udevstart, soit de créer explicitement les fichiers spéciaux de périphériques pour les périphériques en question après son exécution. Cette dernière solution peut être mise en place en ajoutant des scripts d'initialisation complémentaires dans le répertoire /etc/udev/scripts/.