< Un noyau en C qui recharge la GDT | TutoOS | Gérer les interruptions - la mise en oeuvre >
Les interruptions sont des signaux envoyés au processeur pour l'avertir d'événements particuliers. On distingue trois types d'interruptions :
Lorsque le processeur reçoit une interruption, il interrompt la tâche en cours, sauvegarde le contexte de celle-ci, et exécute la routine de service associée à l'interruption (ISR - Interrupt Service Routine) . Une fois la routine executée, le système redonne en général la main à la tâche interrompue.
Quand on presse ou qu'on relache une touche du clavier d'un PC, le contrôleur du clavier envoie une interruption à un chipset chargé de multiplexer les interruptions : le contrôleur d'interruptions. Si l'interruption en question n'est pas masquée, le contrôleur déclenche une interruption matérielle pour prévenir le processeur. Le processeur exécute alors une routine pour traiter l'événement (touche pressée ou relachée). En général, cette routine va interroger le contrôleur du clavier pour savoir quelle touche a été pressée (ou relachée), puis il va éventuellement afficher le caractère correspondant et / ou le stocker dans un buffer. Une fois la routine de traitement de caractère terminée, la tâche interrompue peut reprendre.
Les interruptions matérielles peuvent êtres vues commes des sonnettes que tirées par les périphériques pour prévenir le processeur que quelque chose se passe. Mais si chaque périphérique pouvait envoyer directement un signal au processeur, il faudrait sur celui-ci autant de broches que de périphériques. Le contrôleur d'interruption programmable (PIC, Programmable interrupt controler) est un chipset qui permet d'éviter cela en multiplexant les requète d'interruption envoyées par les périphériques. Le 8259A est l'un des premiers PIC développé par Intel.
Chaque 8259A peut gérer les interruptions de 8 périphériques mais la plupart des PC ont deux contrôleurs cascadés entre eux, ce qui leur permet de gérer jusqu'à 14 périphériques. Le second contrôleur (esclave) est chaîné au premier (contrôleur maître). Un contrôleur peut réaliser des fonctions plus complexes que la simple transmission d'interruptions : il peut masquer des interruptions et définir des priorités de traitement.
Quand un périphériques transmet au 8259A une requète pour une interruption, celle-ci subit un traitement complexe :
Il est possible de paramétrer les deux PIC d'un PC en écrivant dans leurs registres internes via les ports d'entrée/sortie du processeur. On accède au contrôleur maître via les ports 0x20
et 0x21
et au contrôleur esclave via les ports 0xA0
et 0xA1
. En écrivant sur ces ports, on envoie au contrôleur des données qui seront inscrites dans l'un de ses registres.
Il y a deux types de registres :
Pour réinitialiser un contrôleur, il faut remplir les registres d'initialisation ICW1, ICW2, ICW3 et ICW4 en respectant cet ordre. Après avoir transmis les données pour le registre ICW1, le contrôleur attend une valeur à mettre dans le registre ICW2 et ainsi de suite. Si il n'y a qu'un seul contrôleur, ce n'est pas la peine de renseigner les registres ICW3 et ICW4.
Les registres OCW peuvent être rempli à n'importe quel moment après l'initialisation des PIC. Le registre OCW1 permet notament de masquer et demasquer les interruptions.
|0|0|0|1|x|0|x|x| | | +--- avec ICW4 (1) ou sans (0) | +----- un seul contrôleur (1), ou cascadés (0) +--------- declenchement par niveau (level) (1) ou par front (edge) (0)
Par exemple, pour mettre à jour le registre ICW1 du contrôleur maître et du contrôleur esclave :
|x|x|x|x|x|0|0|0| | | | | | +----------------- adresse de base des vecteurs d'interruption
Les bits de poids fort servent à calculer l'adresse de base du vecteur d'interruption. Cette valeur correspond à un offset dans la table IDT (voir plus loin). Par exemple, pour initialiser les adresses de base des vecteurs d'interruption des deux contrôleurs :
Dans l'exemple ci-dessus, les IRQs 0-7 sont mappées pour utiliser les interruptions 0x20-0x27 et les IRQs 8-15 sont mappées sur les interruptions 0x70-0x77. Note : sur les architectures de type x86, les 32 premiers vecteurs (0x0-0x19) sont réservés à la gestion des exceptions. Celà explique pourquoi les IRQs ne sont pas mappées à partir du début de la table.
Ce registre informes les contrôleurs de la façon dont ils sont connectés entre eux :
|x|x|x|x|x|x|x|x| pour le maître | | | | | | | | +------------------ contrôleur esclave rattaché à la broche d'interruption (1), ou non (0)
Remarque : un contrôleur maître peut être rattaché à plusieurs contrôleurs esclaves.
|0|0|0|0|0|x|x|x| pour l'esclave | | | +-------- Identifiant de l'esclave, qui correspond au numéro de broche IR sur le maître
On détermine ici par quelles broches communiquent les deux contrôleurs. Dans l'exemple suivant, le contrôleur esclave est rattaché à la broche 2
du maître :
Ce registre spécifie dans quel mode doit fonctionner le contrôleur. Ceci vient du fait que le 8259A a été conçu pour être générique et supporter différents systèmes :
|0|0|0|x|x|x|x|1| | | | +------ mode "automatic end of interrupt" AEOI (1) | | +-------- mode bufferisé esclave (0) ou maître (1) | +---------- mode bufferisé (1) +------------ mode "fully nested" (1)
Dans le cas présent, nous avons seulement besoin du mode par défaut :
Remarque importante !
Si on initialise les registres d'un contrôleur les uns à la suite des autres, une petite temporisation est nécessaire :
|x|x|x|x|x|x|x|x| | | | | | | | | +------------------ pour chaque IRQ : masque d'interruption établi (1) ou non (0)
Pour modifier le registre IMR permettant de masquer les interruptions :
Ce registre est utilisé essentiellement pour avertir le contrôleur que l'interruption en cours a été traitée et qu'il peut rétablir les interruptions matérielles :
Pour une description des registres OCW2 et OCW3, non utilisés dans le noyau, se réfèrer à la documentation de référence.
La table IDT (Interrupt Descriptor Table) permet d'associer à chaque interruption une routine de service. Chaque entrée de l'IDT est un descripteur particulier qui pointe sur une routine. Lorsqu'une interruption survient, le contrôleur transmet au processeur un vecteur d'interruption. Le processeur l'utilise pour calculer un offset dans l'IDT, charge un descripteur système, puis active la routine de service correspondant :
Les descripteurs dans l'IDT utilisés pour gérer les interruptions sont des descripteurs systèmes d'un type particulier : les interrupt gate.
Note : il est aussi possible d'utiliser des descripteurs du type trap gate. La seule différence est que ces derniers ne désactivent pas les interruptions (via le bit IF du registre EFLAGS) lorsqu'une interruption est reçue.
< Un noyau en C qui recharge la GDT | TutoOS | Gérer les interruptions - la mise en oeuvre >