< Gérer les interruptions - la théorie | TutoOS | Gérer les interruptions du clavier >
Le package contenant les sources est téléchargeable ici : kernel_ManageINT.tgz
Pour naviguer dans l'arborescence : ManageINT
Notez que vous pouvez utiliser la commande diff
pour visualiser les parties modifiées ou ajoutées par rapport aux sources du chapitre précédent :
$ diff -u -r -N ReloadGDT/ ManageINT/
Un certain nombre d'instructions existent en assembleur, et sont nécessaires à l'écriture de notre noyau, mais n'ont pas d'équivalent en C. C'est notamment le cas des instructions cli
, sti
, in
et out
.
Les macros définies dans le fichier io.h permettent de contourner cette difficulté en étendant le jeu d'instructions.
Nous avons déja vu dans les chapitres précédent comment inclure de l'assembleur dans le code C de gcc
avec la directive asm()
. Il faut noter la particularité suivante : gcc
se base sur gas
qui utilise la syntaxe AT&T.
Les descripteurs système de l'IDT gérant les interruptions, dont le fonctionnement est expliqué au chapitre précédent, ont ce modèle :
La structure ci-dessous, définie dans le fichier idt.h
, sert à créer les descripteurs d'interruption. Notez là encore la présence de la directive __attribute__ ((packed))
pour empêcher gcc
d'insérer des octets de padding au sein de la structure :
La fonction init_idt_desc() sert à initialiser les descripteur système.
Par exemple, pour initialiser le descripteur associé à l'IRQ 1 (interruptions clavier), on utilise le code suivant :
0x08
dans la GDT).
Lors d'une interruption, un vecteur est transmis par le PIC au processeur pour exécuter la bonne ISR. Une ISR obéit à deux contraintes particulières :
iret
(au lieu de ret
, utilisé habituellement lors de l'appel à une fonction).
Pour envoyer au contrôleur un message EOI, indiquant que l'interruption en cours a été traitée, on utilise le code assembleur suivant :
En revanche, pour retourner de la routine d'interruption, rien en C ne nous permet d'utiliser iret
. La routine d'interruption appelée doit donc être écrite en assembleur. Par exemple, pour gérer l'interruption de l'IRQ 1 :
Ce code appelle la fonction isr_kbd_int()
qui effectue réellement la gestion de l'interruption du clavier, puis elle envoie un EOI au contrôleur avant de retourner. La partie de code en assembleur ne fait donc qu'encapsuler le code écrit en C dans le fichier interrupt.c.
Les routines en assembleur sont dans le fichier int.asm.
Est-on vraiment obligés d'utiliser pour chaque routine d'interruption ces enveloppes en assembleur et n'est-il pas plus rapide d'inclure dans le code en C une directive du type asm("iret")
? Le problème est qu'en sortant d'une fonction de cette façon, on oublie de restaurer convenablement les registres, dont le pointeur de pile esp
.
init_idt()
La fonction init_idt()
effectue l'initialisation des descripteurs systèmes et le chargement de l'IDT. Étant donné leur similarité, le code nécessaire pour initialiser et charger l'IDT ressemble beaucoup à celui utilisé pour l'initialisation de la GDT. À l'exception des descripteurs associés aux IRQ 0 et 1 (respectivement l'horloge et le clavier), l'ensemble des descripteurs pointent vers un handler d'interruption par défaut :
init_gdt()
réinitialise la GDT puis la fonction init_idt()
configure et charge la table IDT des descripteurs d'interruptions.
init_pic()
. Cette fonction, définie dans le fichier pic.c
, initialise les contrôleurs d'interruption maître et esclave de la façon décrite au chapitre précédent.
sti
.
Le schéma ci-dessous résume l'organisation des données en mémoire physique après les initialisations :
$ tar xfz kernel_ManageINT.tgz $ cd ManageINT $ make
A l'exécution du noyau, un tic est affiché toutes les 100 interruptions de l'horloge. En revanche, pour le moment, l'appui d'une touche du clavier ne fonctionne qu'une seule fois. Nous verrons plus tard comment gérer correctement le clavier :
< Gérer les interruptions - la théorie | TutoOS | Gérer les interruptions du clavier >