Toujours préemptible et toujours interruptible
L’objectif de la conception prééptible et interruptible du système d’exploitation est d’optimiser les performances du système. N’importe quel thread peut être préempté par un thread avec une priorité plus élevée, et la routine de service d’interruption (ISR) d’un pilote peut être interrompue par une routine qui s’exécute à un niveau de demande d’interruption (IRQL) supérieur.
Le composant du noyau détermine quand une séquence de code s’exécute, selon l’un de ces critères de hiérarchisation :
Schéma de priorité d’exécution défini par le noyau pour les threads.
Chaque thread du système a un attribut de priorité associé. En général, la plupart des threads ont des attributs de priorité variable : ils sont toujours préeptibles et sont planifiés pour exécuter le round robin avec tous les autres threads qui sont actuellement au même niveau de priorité. Certains threads ont des attributs de priorité en temps réel : ces threads critiques s’exécutent jusqu’à l’achèvement, sauf s’ils sont préemptés par un thread qui a un attribut de priorité en temps réel plus élevé. L’architecture Microsoft Windows ne fournit pas de système en temps réel par nature.
Quel que soit son attribut de priorité, n’importe quel thread du système peut être préempté lorsque des interruptions matérielles et certains types d’interruptions logicielles se produisent.
Niveau de demande d’interruption défini par le noyau (IRQL) auquel un vecteur d’interruption particulier est affecté sur une plateforme donnée.
Le noyau hiérarchise les interruptions matérielles et logicielles afin que certains codes en mode noyau, y compris la plupart des pilotes, s’exécutent à des IRQL plus élevés, ce qui lui donne une priorité de planification plus élevée que les autres threads du système. L’IRQL particulier auquel s’exécute un morceau de code de pilote en mode noyau est déterminé par la priorité matérielle de son appareil sous-jacent.
Le code en mode noyau est toujours interruptible : une interruption avec une valeur IRQL plus élevée peut se produire à tout moment, ce qui entraîne l’exécution immédiate d’un autre élément de code en mode noyau qui a un IRQL attribué par le système supérieur à être exécuté immédiatement sur ce processeur. Toutefois, lorsqu’un morceau de code s’exécute à un IRQL donné, le noyau masque tous les vecteurs d’interruption avec une valeur IRQL inférieure ou égale sur le processeur.
Le niveau IRQL le plus bas est appelé PASSIVE_LEVEL. À ce niveau, aucun vecteur d’interruption n’est masqué. Les threads s’exécutent généralement à IRQL=PASSIVE_LEVEL. Les niveaux d’IRQL supérieurs suivants concernent les interruptions logicielles. Ces niveaux incluent APC_LEVEL, DISPATCH_LEVEL ou, pour le débogage du noyau, WAKE_LEVEL. Les interruptions d’appareil ont des valeurs IRQL encore plus élevées. Le noyau réserve les valeurs IRQL les plus élevées pour les interruptions critiques du système, telles que celles de l’horloge système ou des erreurs de bus.
Certaines routines de prise en charge système s’exécutent à IRQL=PASSIVE_LEVEL, soit parce qu’elles sont implémentées en tant que code paginable ou qu’elles accèdent à des données paginables, soit parce que certains composants en mode noyau configurent leurs propres threads.
De même, certaines routines de pilotes standard s’exécutent généralement à IRQL=PASSIVE_LEVEL. Toutefois, plusieurs routines de pilotes standard s’exécutent à IRQL=DISPATCH_LEVEL ou, pour un pilote de niveau le plus bas, à l’IRQL de l’appareil (également appelé DIRQL). Pour plus d’informations sur les IRQL, consultez Gestion des priorités matérielles.
Chaque routine d’un pilote est interruptible. Cela inclut toutes les routines qui s’exécutent à un IRQL supérieur à PASSIVE_LEVEL. Toute routine qui s’exécute sur un IRQL particulier conserve le contrôle du processeur uniquement si aucune interruption pour un IRQL plus élevé ne se produit pendant l’exécution de cette routine.
Contrairement aux pilotes de certains anciens systèmes d’exploitation d’ordinateurs personnels, l’ISR d’un pilote Microsoft Windows n’est jamais une routine complexe et volumineuse qui effectue la plupart du traitement des E/S du pilote. Cela est dû au fait que la routine de service d’interruption (ISR) d’un pilote peut être interrompue par une autre routine (par exemple, par l’ISR d’un autre pilote) qui s’exécute à un IRQL plus élevé. Ainsi, l’ISR du pilote ne conserve pas nécessairement le contrôle d’un processeur, sans interruption, du début de son chemin d’exécution à la fin.
Dans les pilotes Windows, un ISR enregistre généralement les informations d’état du matériel, met en file d’attente un appel de procédure différée (DPC), puis se ferme rapidement. Plus tard, le système met la file d’attente du DPC du pilote afin que le pilote puisse effectuer des opérations d’E/S à un niveau d’IRQL (DISPATCH_LEVEL) inférieur. Pour de bonnes performances globales du système, toutes les routines qui s’exécutent à des IRQL élevés doivent abandonner rapidement le contrôle du processeur.
Dans Windows, tous les threads ont un contexte de thread. Ce contexte se compose d’informations qui identifient le processus propriétaire du thread, ainsi que d’autres caractéristiques telles que les droits d’accès du thread.
En général, seul un pilote de niveau supérieur est appelé dans le contexte du thread qui demande l’opération d’E/S actuelle du pilote. Un pilote de niveau intermédiaire ou de niveau le plus bas ne peut jamais supposer qu’il s’exécute dans le contexte du thread qui a demandé son opération d’E/S actuelle.
Par conséquent, les routines de pilotes s’exécutent généralement dans un contexte de thread arbitraire, le contexte de tout thread actuel lorsqu’une routine de pilote standard est appelée. Pour des raisons de performances (pour éviter les commutateurs de contexte), très peu de pilotes configurent leurs propres threads.