Chapitre 1
photo ____________________
Une introduction aux pilotes de périphériques
L'un des nombreux avantages des systèmes d'exploitations libres, écrits comme Linux, est que leur intérieur est ouvert à tous pour l'observation. Ce système d'exploitation, initialement un domaine sombre et mystérieux dont le code était réservé à un petit nombre de programmeurs, peut désormais être lu, compris et modifié par n'importe qui ayant les quelques compétences requises. Linux a aidé à démocratiser les systèmes d'exploitations. Le noyaux linux contient une quantité de code complexe et énorme qui demanderai même aux kernel hackers de savoir par quel bout s'attaquer à la chose sans être dépassé par la complexité. Souvent, l'étude des pilotes de périphériques, nous donne ce point d'entrée.
Les pilotes de périphériques ont un rôle spécifique dans le noyau linux. Ce sont différentes boîtes noires relatives à une partie matérielle particulière répondant à une interface de programmation (API) bien définie. Il cache complètement les détails de fonctionnement du périphérique.
Les actions utilisateurs sont réalisées par définition d'un ensemble d'appels standardisés qui sont indépendants du pilote spécifique ; reliant ces appels aux opérations spécifiques du pilote pour agir sur le vrai matériel. Ceci est donc le rôle du pilote de périphérique. Cette interface de programmation est telle que les pilotes peuvent être compilés séparément du reste du
noyau et "connectés" au besoin au moment de l'exécution. Cette modularité rend l'écriture de pilotes Linux facile, au point que des centaines d'entre-eux sont maintenant disponibles.
Il y a beaucoup de raisons de s'intéresser à l'écriture de pilotes de périphériques sous Linux. La rapidité à laquelle le nouveau matériel devient disponible (et obsolète!) guarantie que les programmeurs de pilotes seront occupés pour le futur proche. Certaines personnes peuvent avoir besoin de connaître le sujet des pilotes de périphérique afin d'avoir accès à un matériel particulier les intéressant. Les fabricants de matériel, en réalisant un pilote Linux pour leurs produits, peuvent ajouter l'importante et croissante part des utilisateurs de Linux à leurs marchés potentiels. Et la nature Open-Source du système Linux implique que si le programmeur du pilote le souhaite, le source d'un driver soit rapidement publié à des millions d'utilisateurs.
Ce livre vous apprend comment écrire vos propres pilotes et comment vous situer dans les parties relatives du noyau. Nous avons choisi une approche indépendante du matériel (device-independant). Les techniques de programmation sont présentées, tant que c'est possible, sans être spécifiques à un pilote particulier. Chaque pilote est différent. En tant que programmeur de pilotes, vous devez bien connaître et comprendre votre matériel spécifique. Mais la pluspart des principales techniques de base sont les mêmes pour tous les pilotes. Ce livre ne peut vous enseigner sur votre matériel, mais il vous donne le savoir necessaire pour faire fonctionner ce matériel.
En apprenant à écrire des pilotes, vous apprenez beaucoup sur le noyau linux en général. Ceci peut vous aider à comprendre comment fonctionne votre ordinateur et pourquoi il n'est pas toujours aussi rapide que vous le souhaiteriez ou ne fait pas vraiment ce que vous voulez. Nous introduisons de nouvelles idées graduellement, en commencant par de très simples pilotes comme base de construction. Tout nouveau concept est accompagné d'un échantillon de code qui ne nécessite pas de matériel spécial pour être testé.
Ce chapitre ne porte pas pour l'instant sur l'écriture de code. Nous instroduisont d'abord, des concepts de base sur le noyau linux que vous serez heureux de connaître plus tard, quand nous entrerons dans la programmation.
Le rôle d'un pilote de périphérique
En tant que programmeur, vous êtes à même de faire vos propres choix de pilotes, en choisissant un compromis acceptable entre le temps de programmation nécessaire et la souplesse du résultat. Bien que ça puisse sembler étrange de dire qu'un pilote soit "souple" nous aimons ce mot car il souligne le fait qu'un pilote nous procure un mécanisme, et non une politique.
La distinction entre "mécanisme" et "règles" est l'une des meilleures idées du concept Unix. La plupart des problèmes de programmation peuvent tout à fait être séparés en deux questions:
"De quelles fonctionnalités avont nous besoin?" (le mécanisme) et "comment ces fonctionnalités peuvent être utilisées?" (la politique). Si les deux points en question sont abordés par deux parties distinctes du programme, ou même par des programmes entièrement différents, le package de l'application sera plus facile à développer et à adapter à des besoins particuliers.
Par exemple, la gestion de l'affichage graphique sous Unix est partagée entre le serveur X qui connait le matériel et offre une interface unifiée aux programmes utilisateurs et les gestionnaire de fenêtres et de sessions qui implémentent une politique particulière sans rien connaître du matériel. On peut utiliser le même gestionnaire de fenêtre sur des matériels différents et des utilisateurs différents peuvent exécuter plusieurs configurations sur la
même station de travail. Même des environnements de bureau totalement différents comme KDE et GNOME peuvent cohabiter sur le même système. Un autre exemple concerne la structure en couches de la gestion du réseau TCP/IP: le système d'exploitation offre l'abstraction de socket, qui n'implémente pas de politique concernant les données à transférer, alors que d'autres serveurs sont en charge des services (et leur politique associée).
Qui plus est, un serveur comme ftpd nous procure le mécanisme de transfert de fichiers, alors que les utilisateurs peuvent utiliser n'importe quel client ftp de leur choix. Il existe à la fois des clients en ligne de commande et des clients en mode graphique et n'importe qui a la possibilité d'écrire une nouvelle interface utilisateur pour transférer des fichiers.
Quand il s'agit de pilotes, la même séparation entre le mécanisme et la politique s'applique. Le pilote de la disquette est exempt de toute politique ; sont rôle est seulement de présenter la disquette comme un tableau continu de blocs de données. Les niveaux plus hauts du système procurent des politiques ; comme qui à le droit d'accéder à la disquette, si la disquette est accessible directement ou via un système de fichiers et si tous les utilisateurs ont le droit de monter la disquette.
À partir du moment où différents environnements ont besoin d'utiliser le matériel de différentes manières, il est important d'avoir une politique aussi libre que possible.
En écrivant des pilotes un programmeur devrait être particulièrement attentif un concept fondamental : écrire du code noyau pour accéder au matériel mais ne pas forcer une politique pour l'utilisateur, à partir du moment où des utilisateurs différents ont des besoins différents. Le pilote devrait permettre l'accès au matériel sans s'occuper de son utilisation dans les applications. Un pilote est donc souple s'il procure un accès aux fonctionnalités du matériel sans ajouter de contraintes. Quelquefois, toutefois quelques décisions de politique doivent être appliquées. Par exemple un pilote d'entrée/sortie numérique ne peut procurer qu'un accès d'un octet de long au matériel afin d'éviter le code necéssaire pour manipuler les bits individuels.
Note : (compréhension technique à élucider)
Vous pouvez aussi examiner votre pilote sous un autre angle : c'est une couche logicielle qui réside entre les applications et le périphérique en question.
Ce rôle privilégié du pilote permet au développeur de choisir précisément comment le périphérique doit apparaître: plusieurs pilotes peuvent offrir plusieurs fonctionnalités, même pour le même périphérique. L'étude actuelle d'un pilote doit être un compromis entre plusieurs considérations. Pour l'instant un périphérique unique peut être utilisé par plusieurs programmes différents, et le programmeur de pilote à totale liberté pour la gestion du partage de cette ressource. Vous pourriez mettre en place une gestion de mémoire sur le périphérique indépendamment de ses propriétés matérielles ou bien vous pourriez procurer une librairie utilisateurs afin d'aider les programmeurs d'applications à mettre en place de nouvelles règles au dessus des primitives disponibles, etc. Ce qui est à considérer en priorité c'est le change entre le désir de présenter autant d'options que possibles à l'utilisateur et le temps que vous avez pour écrire le pilote, et aussi la nécessité que les choses restent simples afin d'éviter que des erreurs ne s'insèrent insidieusement.
Les pilotes sans politique ont un nombre de caractéristiques typiques. Celles-ci incluent le support pour à la fois, les opérations synchrones et asynchrones, la possibilité d'être ouverts plusieurs fois, la possibilité d'exploiter toutes les performances du matériel et l'absence de couches logicielle pour "simplifier les choses" ou fournir des opérations sur mesure. Les pilotes de cette catégorie ne fonctionnent pas seulement mieux pour leurs utilisateurs finaux, mais deviennent aussi plus faciles à écrire et à maintenir, dans le même temps. Être libre de politique est actuellement un objectif commun pour les modélisateurs de code.
Beaucoup de pilotes de périphériques, certes, sont réalisés en même temps que des programmes utilisateurs pour faciliter la configuration et l'accès au périphérique cible. Ces programmes vont des simples utilitaires aux applications graphiques complètes. Des exemples comprenant le programme
tunelp, qui ajuste le comportement du port parallèle de l'imprimante, et de l'utilitaire graphique
cardctl qui fait partie du paquet pilote PCMCIA. Souvent une librairie cliente est aussi fournie ; laquelle fournie des fonctionnalités qui n'ont pas à être intégrées dans le pilote lui-même.
L'étendue de ce libre concerne le noyau, ainsi nous n'abordons pas la question du comment soit avec les programmes d'applications soit avec les librairies de support. Parfois nous parlons des différentes règles et comment les supporter, mais nous n'entrerons pas plus en détail sur la manière dont le programmes utilisent le périphérique or les règles qu'ils mettent en oeuvre. Vous devriez comprendre, de toute façon, que les programmes utilisateurs font partie intégrante d'un paquet logiciel et que même les paquets exempts de politique sont distribués avec des fichiers de configuration qui appliquent un procédé par défaut au mécanismes sous-jacents.
Décomposition du Noyau
Dans un système Unix, plusieurs processus concurrents s'occupent de différentes tâches. Chaque processus demande des ressources systèmes, que ce soit microprocesseur, mémoire, connexion au réseau, ou quelque autre ressource. Le noyau est la grosse boule de code exécutable en charge de manipuler de telles requêtes. Bien que la distinction entre les différentes tâches du noyau n'est pas toujours clairement définie, le rôle du noyau peut être séparé (comme sur la Figure 1-1) en ces différentes parties:
Gestion de processus
Le noyau est en charge de la création et de la destruction des processus ainsi que de la gestion de leur connexion au monde extérieur (entrée et sortie). La communication entre les différents processus (au moyen de signaux, des pipes, ou des primitives IPC Inter Process Communication) est basique à l'ensemble des capacités des systèmes et est aussi géré par le noyau. De plus, le
scheduler, qui contrôle la façon dont les processus partagent le CPU, fait partie de la gestion de processus. Plus généralement, l'activité de gestion de processus par le noyau implémente l'abstraction de plusieurs processus au dessus d'un simple CPU
or quelques-uns d'entre eux.
Gestion de mémoire
La mémoire est une ressource majeure et la règle à appliquer est critique pour la performance du système. Le noyau met en place un espace d'adressage virtuel pour n'importe quel et tous les processus au dessus de la limite des ressources disponibles. Les différentes parties du noyau interagissent avec le sous-système de gestion de mémoire au moyen d'un ensemble d'appel de fonctions, allant de la simple paire malloc/free à des fonctionnalités plus complexes.
Système de fichier
Unix est fortement basé sur le concept de système de fichier; presque tout dans Unix peut être traité comme un fichier. Le noyau fabrique une structure au dessus du matériel non structuré, et l'abstraction de fichier résultante est fortement utilisée à travers l'ensemble du système. Aussi, Linux supporte plusieurs types de systèmes de fichiers, qui correspondent, à des façons différentes d'organiser les données sur le support physique. Par exemple, les disques peuvent être formatés avec le système de fichier Linux-Standard ext3, le système de fichier FAT utilisé communément, ou plusieurs autres.
Contrôle de périphérique
Chaque opérations système ou presque est liée à un périphérique physique. À l'exception du microprocesseur, de la mémoire et très peu d'autres entités, toutes les opérations de contrôle de périphérique sont réalisées par du code qui est spécifique au périphérique que l'on adresse. Ce code est ce qu'on appelle un pilote de périphérique. Le noyau doit comprendre un pilote de périphérique pour chaque matériel périphérique présent sur un système, du disque dur au clavier et à la souris. Cet aspect des fonctions du noyau est notre principal intérêt dans ce document.
Réseau
Le réseau doit être pris en charge par le système d'exploitation, parce que la plupart des opérations réseau ne sont pas spécifique à un processus; les paquets entrants sont des évènements asynchrones. Les paquets doivent être collectés, identifiés, et dispatchés avant qu'un processus ne puisse y prêter attention. Le système est en charge de la livraison des paquets de données aux programmes et aux interfaces réseau, et il doit contrôler l'exécution des programmes en fonction de l'activité réseau. En plus, toutes les opérations de routage et de résolution d'adresse sont implémentées à l'intérieur du noyau.
Modules chargeables
L'un des avantages de Linux est la possibilité d'étendre pendant l'exécution, le choix de fonctionnalités offertes par le noyau. Cela signifie que vous pouvez ajouter des fonctionnalités au noyau (et aussi les enlever) pendant que le système est démarré et en actif.
Toute partie de code qui peut être ajoutée au noyau pendant son fonctionnement s'appelle un module. Le noyau Linux offre des supports pour différents types (ou classes) de modules, incluant, mais sans se limiter, les pilotes de périphérique. Chaque module est constitué de code objet (non lié en tant qu'exécutable final) qui peut être lié dynamiquement au noyau actif par le programme insmod et peut être détaché par le programme rmmod.
La figure 1.1 identifie différents classes de modules en charge de fonctions spécifiques --- on dit d'un module qu'il appartient à une classe spécifique en fonction de la fonctionnalité qu'il procure. La représentation des modules dans la Figure 1.1 couvre les classes les plus importantes, mais est loin d'être complète car de plus en plus de fonctionnalités sont modularisées dans Linux.
Classes de périphériques et modules
La façon dont Linux reconnaît les périphériques se décompose en trois types fondamentaux. Normalement, tout module implémente l'un des ces types, ainsi classifiable comme, un module en mode caractère (char module), en mode blocs (block module), ou module réseau (network module). La division des modules en 3 types, ou classes, n'est pas stricte ; le programmeur peut choisir de faire des modules très gros incluant plusieurs pilotes dans un seul bloc de code. Les bons programmeurs, néanmoins, ont coutume de faire un module pour chaque fonctionnalité qu'ils implémentent, car la décomposition est un élément clé pour l'optimisation et l'évolution.
Les trois classes sont :
Périphériques en mode caractères
Un périphérique caractère (char) est un périphérique qui peut être vu comme flux d'octets (comme un fichier) ; un pilote en mode caractère à la charge de mettre en œuvre ce procédé. Un tel pilote généralement, fait appel finalement aux fonctions systèmes
open, close, read et write. La console texte (/dev/console) et les ports séries (/dev/ttyS0 et autres) sont des exemples de périphérique en mode caractères, étant donné qu'ils sont bien représentés par "l'abstraction de flux". Les périphériques caractères sont vus par représentations de noeuds du système de fichiers, comme /dev/tty1 et /dev/lp0. La seule différence notoire entre un périphérique caractère et que vous pouvez toujours vous déplacer en arrière, etc. Pour l'instant, ceci s'applique en général aux capteurs de trames, ou les applications peuvent avoir accès à une image complète en utilisant mmap ou lseek.
Périphérique en mode blocs
Comme les périphériques caractères, les périphériques en mode blocs sont accessibles par des noeuds sur le système de fichiers dans le répertoire /dev. Un périphérique bloc est un périphérique (e.g: un disque) qui peut héberger un système de fichiers. Dans la plupart de systèmes Unix, un périphérique en mode bloc ne peut que manipuler des opération Entrées/Sorties qui transfèrent un ou plusieurs blocs complets, qui sont fréquemment de 512 octets (ou une plus grande taille du double). Linux, quand à lui, autorise à l'application de lire un bloc périphérique comme un bloc caractères --- il permet le transfert de n'importe qu'elle nombre d'octets en une fois. Il en résulte que les périphériques blocs et caractères ne diffèrent que dans la façon dont les données sont gérées en interne dans le noyau et par la suite dans l'interface logicielle du pilote noyau. Comme un périphérique caractères, chaque périphérique en mode bloc est accessible au travers d'un noeud du système de fichier, et la différence entre eux est transparente pour l'utilisateur. Les pilotes en mode blocs ont une interface complètement différente au niveau du noyau que les pilotes en mode caractères.
Interfaces réseaux
Toute transmission réseau est faite au travers d'une interface, qui est, un périphérique capable d'échanger des données avec d'autres hôtes. Habituellement, une interface est un périphérique matériel, mais il peut aussi être un périphérique purement logiciel, comme l'interface loopback. Une interface réseau est en charge d'envoyer et recevoir des paquets de données, piloté par le sous-système réseau du noyau, sans avoir connaissance de la manière dont les transmissions individuelles tracent les paquets en cours de transfert. Plusieurs connexions réseau (particulièrement celles utilisant TCP) sont orientées flux, mais les périphériques réseau sont, habituellement, conçus pour la transmission et la réception de paquets. Un pilote réseau ne connaît rien des connexions individuelles, il ne fait que de manipuler des paquets. N'étant pas un périphérique orienté flux, une interface réseau n'est pas facilement liée à un noeud du système de fichier, comme /dev/tty1 l'est. La méthode Unix pour procurer un accès aux interfaces est encore en leur assignant un nom unique (comme eth0), mais ce nom n'a pas d'entrée correspondante dans le système de fichier /dev. La communication entre le noyau et un périphérique réseau est totalement différente de celle utilisée avec les pilotes caractères et blocs. Au lieu de
read et
write le noyau fait appel à des fonctions relatives à la transmission de paquets.
Il y a d'autres moyens de classifier les modules qui sont orthogonaux aux types de périphérique ci-dessus. En général, quelques types de pilotes fonctionnent avec des couches additionnelles qui font parties des fonctions du support noyaux pour un type donné de périphérique. Par exemple, si on parle des modules du bus série universel (Universal Serial Bus), les modules serial, les modules SCSI et ainsi de suite. Tout périphérique USB est piloté par un module USB qui opère au moyen du sous-système USB, mais le périphérique lui-même apparaît dans le système comme un périphérique caractère (a port série USB, dit), un périphérique blocs (un lecteur de cartes mémoire USB), ou une interface réseau.
Les autres classes de pilotes de périphériques ont été ajoutées au noyau récemment, comprenant les pilotes FireWire et les pilotes I2O. En même temps qu'ils manipulaient les pilotes USB et SCSI, les développeurs de noyau ont recueillit des propriétés de large classe (class-wide), les ont exportées aux fournisseurs de modules afin d'éviter le travail en double et les bugs,et aussi simplifier et renforcer le processus pour écrire de tel pilotes.
En plus des pilotes de périphériques, d'autres fonctions, à la fois matérielles et logicielles, sont modularisées dans le noyau. Un exemple commun est le système de fichier. Le type de système de fichier détermine comment l'information est organisée sur un périphérique blocs afin de représenter un arbre de répertoires et de fichiers. Une telle entité n'est pas un pilote de périphérique, par le fait qu'il n'y a pas de périphérique explicite avec la manière dont l'information y est redirigée; c'est plutôt le type de système de fichier qui est un pilote logiciel, car il fait correspondre les structures de données bas niveau à des structures de données de haut niveau. C'est le système de fichier qui détermine combien de temps un nom de fichier peut exister et quelle information sur chaque fichier est enregistrée dans une entrée de répertoire. Le module du système de fichier doit implémenter le plus bas niveau d'appels système qui accède aux répertoires et fichiers en cartographiant les noms de fichiers et les chemins (et d'autre information comme les modes d'accès) aux structures de données contenues dans les blocs de données. Une telle interface est totalement indépendante du transfert de données en cours vers et du disque (ou autre médium), qui est accompli par un pilote de périphérique en mode blocs.
Si vous vous posez la question : à quel point un système Unix dépend du système de fichiers sous-jacent, vous vous rendrez compte qu'un tel concept logiciel est vital pour les opérations systèmes. La capacité de décoder l'information système de fichier est ancrée au plus bas niveau de la hiérarchie du noyau est d'une importance majeure; même si vous écrivez un pilote en mode blocs pour votre nouveau CD-ROM, il est inutile si vous n'êtes pas capable de lancer les commandes ls ou cp pour les données qu'il contient. Linux supporte le concept d'un module système de fichier, dont l'interface logicielle déclare les différentes opérations qui peuvent être exécutées sur l'inode, le répertoire, le fichier et le superbloc du système de fichier. Il est très peu courant actuellement qu'un programmeur ai besoin d'écrire un module système de fichier, car le noyau officiel inclue déjà le code pour les types de systèmes de fichiers les plus importants.
La question de la sécurité
Partager