Comment le processeur trouve-t-il le code du noyau après une interruption?

13

Lorsqu'une interruption se produit, le processeur prévaut le processus en cours et appelle le code du noyau pour gérer l'interruption. Comment le processeur sait-il où entrer dans le noyau?

Je comprends qu'il existe des gestionnaires d'interruptions qui peuvent être installés pour chaque ligne d'interruption. Mais puisque le processeur n'exécute que la «logique câblée», il doit exister un endroit prédéfini qui pointe vers un gestionnaire d'interruption lui-même, ou un code qui s'exécute avant le gestionnaire (car il peut y avoir plusieurs gestionnaires pour une ligne d'interruption, je suppose que le dernier).

Philipp Murry
la source

Réponses:

13

Au démarrage, le noyau initialisera une table de vecteur d'interruption (appelée table de descripteur d'interruption ou IDT sur x86) qui pointe vers un gestionnaire d'interruption pour chaque ligne.

Avant le 80286, l'IDT était toujours stocké à une adresse fixe; à partir du 80286, l'IDT est chargé à l'aide de l' LIDTinstruction.

Les tables de vecteurs d'interruption pointent vers un seul gestionnaire par ligne d'interruption; cela étant dit, un noyau pourrait choisir, par exemple, de fournir un gestionnaire d'interruption qui exécute plusieurs autres routines d'interruption, ou de fournir un seul gestionnaire qui couvre certaines ou toutes les interruptions. Linux fait ces choses en fournissant un gestionnaire d'interruption générique qui détermine quelle ligne d'interruption a été appelée et trouve le gestionnaire en aval approprié à appeler.

Adam Maras
la source
1
de sorte que le processeur utilise la ligne d'interruption comme index de l'IDT, place l'entrée dans le PC et commence à s'exécuter? mais n'y a-t-il pas une fonction générique qui s'exécute avant tous les gestionnaires d'interruption? pour linux, ce serait do_IRQ (). Est-ce la fonction vers laquelle pointe chaque entrée IDT, quelle que soit la ligne d'interruption?
Philipp Murry
@PhilippMurry oui. Le noyau utilise ensuite son propre ensemble de gestionnaires d'interruption (dont il peut y en avoir plus d'un par ligne) pour gérer réellement l'interruption, avant de revenir au code précédemment exécuté.
Adam Maras
ok, il y a donc en fait deux types de gestionnaires d'interruption: ceux que le processeur appelle (toujours do_IRQ ()), et ceux que le noyau appelle (celui que j'ai enregistré via request_irq ()). pourriez-vous peut-être ajouter ceci à votre réponse? je pense que je l'accepterai :) merci beaucoup
Philipp Murry
1
@PhilippMurry Je ne suis pas un expert de la façon dont Linux gère les interruptions (et comment les développeurs du noyau exploitent ce système), mais j'ai ajouté quelques informations plus générales sur la façon dont les noyaux peuvent avoir leur propre gestion ISR.
Adam Maras
12

Oui, il y a un endroit prédéfini qui contient l'adresse du code vers lequel sauter: un vecteur d'interruption . Selon le processeur, il peut s'agir d'un emplacement spécifique en mémoire physique (8088), d'un emplacement spécifique en mémoire virtuelle, d'un registre de processeur, d'un emplacement en mémoire indiqué par un registre (ARM, 386),…

Les détails varient selon les différents processeurs, mais les principaux éléments communs à la gestion d'une interruption dans le processeur sont:

  • Masque les interruptions (de sorte que toute interruption ultérieure devra attendre).
  • Réglez le mode processeur sur le mode noyau ou interruption (si le processeur possède de tels modes).
  • Enregistrez la valeur du compteur de programmes dans un emplacement connu (registre ou mémoire).
  • Peut-être enregistrer la valeur d'autres registres ou basculer entre les banques de registres).
  • Exécutez l'instruction suivante (à la nouvelle valeur PC).
Gilles 'SO- arrête d'être méchant'
la source
1

Les deux autres réponses (au moment de la rédaction) parlent des interruptions et de l'IDT. C'est correct, cependant, sur un processeur Intel-esque moderne, il n'y a pas moins de trois façons d'appeler un noyau.

Méthode n ° 1: interruptions.

Ceci est expliqué ci-dessus. Vous configurez une entrée dans la table des descripteurs d'interruption / vecteur d'interruption, puis exécutez une interruption logicielle pour entrer dans le noyau.

Le principal avantage de cette méthode est qu'un noyau typique doit de toute façon être capable de gérer les interruptions et qu'il fonctionne sur du matériel archaïque.

Méthode n ° 2: appelez les portes.

Une porte d'appel est un type spécial de sélecteur de segment. La cible de l'appel doit être chargée dans la table de descripteurs de segment global ou local (GDT et LDT respectivement). Si vous effectuez ensuite une instruction d'appel distant en utilisant la porte d'appel comme segment (le décalage de l'appel est ignoré), cela vous permet d'appeler un code plus privilégié. Les portes d'appel sont extrêmement flexibles; l'architecture IA-32 possède quatre niveaux de privilèges et les portes d'appel vous permettent d'appeler n'importe quel niveau.

Je ne crois pas que Linux ait jamais utilisé de portes d'appel, mais Windows 95 l'a fait. Les services du noyau Win95 ( krnl386.exeet kernel.dll) fonctionnaient en fait en mode utilisateur (anneau 3). Le niveau de privilège le plus élevé (anneau 0) n'était utilisé que pour les pilotes et un micro-noyau qui effectuait uniquement la commutation de processus. L'appel aux chauffeurs s'est fait à l'aide de portes d'appel. Cela a permis au code 16 bits hérité (dont il y en avait beaucoup!) D'utiliser des pilotes Win95 en utilisant simplement un appel distant standard, comme ils l'ont toujours fait.

Une protection inadéquate de la table des descripteurs globaux a été à l'origine de plusieurs exploits de Windows 95, qui ont réussi à installer leurs propres portes d'appel en écrivant sur la mémoire.

Méthode n ° 3: SYSCALL / SYSRET et SYSENTER / SYSEXIT

Ce sont deux ensembles d'instructions, inventés indépendamment par AMD et Intel, mais ils font essentiellement la même chose. SYSCALL / SYSRET est arrivé en premier et était uniquement AMD, SYSENTER / SYSEXIT était Intel, mais AMD l'implémente maintenant. Je vais donc décrire SYSENTER / SYSEXIT.

Contrairement aux portes d'appel, SYSENTER ne peut être utilisé que pour transférer vers la sonnerie 0 et ne peut être transféré que vers un seul emplacement. Cependant, il présente l'avantage d'une latence extrêmement faible car contrairement à un appel ou une interruption, il ne touche pas la pile.

L'emplacement de transfert est configuré à l'aide de trois registres spécifiques au modèle: un pour les informations de segment et un pour le pointeur d'instruction et le pointeur de pile du code du noyau. Parce que rien n'est "poussé" sur la pile, le code du mode utilisateur est chargé d'indiquer au noyau où retourner en passant le pointeur d'instruction de retour et le pointeur de pile dans les registres. Le noyau est responsable de la restauration du pointeur de pile et l'instruction SYSEXIT restaure le pointeur d'instruction.

Plus d'informations sur les instructions SYSENTER et SYSEXIT.

Pseudonyme
la source