Objets sémaphores
Tout pilote peut utiliser un objet sémaphore pour synchroniser les opérations entre ses threads créés par le pilote et d’autres routines de pilotes. Par exemple, un thread dédié au pilote peut se placer dans un état d’attente lorsqu’il n’y a aucune demande d’E/S en attente pour le pilote, et les routines de répartition du pilote peuvent définir le sémaphore sur l’état Signaled juste après qu’ils mettent en file d’attente une IRP.
Les routines de répartition des pilotes de niveau supérieur, qui sont exécutées dans le contexte du thread demandant une opération d’E/S, peuvent utiliser un sémaphore pour protéger une ressource partagée entre les routines de distribution. Les routines de distribution de pilotes de niveau inférieur pour les opérations d’E/S synchrones peuvent également utiliser un sémaphore pour protéger une ressource partagée entre ce sous-ensemble de routines de répartition ou avec un thread créé par le pilote.
Tout pilote qui utilise un objet sémaphore doit appeler KeInitializeSemaphore avant d’attendre ou de libérer le sémaphore. La figure suivante illustre comment un pilote avec un thread peut utiliser un objet sémaphore.
Comme le montre la figure précédente, un tel pilote doit fournir le stockage de l’objet sémaphore, qui doit être résident. Le pilote peut utiliser l’extension de périphérique d’un objet de périphérique créé par le pilote, l’extension de contrôleur si elle utilise un objet contrôleur ou un pool non paginé alloué par le pilote.
Lorsque la routine AddDevice du pilote appelle KeInitializeSemaphore, elle doit passer un pointeur vers le stockage résident du pilote pour l’objet sémaphore. En outre, l’appelant doit spécifier un Nombre pour l’objet sémaphore, comme illustré dans la figure précédente, qui détermine son état initial (différent de zéro pour Signaled).
L’appelant doit également spécifier une limite pour le sémaphore, qui peut être l’une des suivantes :
Limite = 1
Lorsque ce sémaphore est défini sur l’état Signaled, un seul thread attendant que le sémaphore soit défini à l’état Signaled devient éligible à l’exécution et peut accéder à toute ressource protégée par le sémaphore.
Ce type de sémaphore est également appelé sémaphore binaire , car un thread a ou n’a pas d’accès exclusif à la ressource protégée par sémaphore.
Limite > 1
Lorsque ce sémaphore est défini à l’état Signaled, un certain nombre de threads attendant que l’objet sémaphore soit défini sur l’état Signaled deviennent éligibles à l’exécution et peuvent accéder à n’importe quelle ressource protégée par le sémaphore.
Ce type de sémaphore est appelé sémaphore de comptage , car la routine qui définit le sémaphore à l’état Signaled spécifie également combien de threads en attente peuvent avoir leurs états d’attente à prêt. Le nombre de ces threads en attente peut être la limite définie lorsque le sémaphore a été initialisé ou un nombre inférieur à cette limite prédéfinie.
Peu de pilotes de périphérique ou intermédiaires ont un seul thread créé par un pilote ; encore moins ont un ensemble de threads qui peuvent attendre qu’un sémaphore soit acquis ou libéré. Peu de pilotes fournis par le système utilisent des objets sémaphores et, parmi ceux qui le font, encore moins utilisent un sémaphore binaire. Bien qu’un sémaphore binaire puisse sembler similaire aux fonctionnalités d’un objet mutex, un sémaphore binaire ne fournit pas la protection intégrée contre les interblocages qu’un objet mutex a pour les threads système s’exécutant sur des machines SMP.
Une fois qu’un pilote avec un sémaphore initialisé est chargé, il peut synchroniser les opérations sur le sémaphore qui protège une ressource partagée. Par exemple, un pilote avec un thread dédié au périphérique qui gère la mise en file d’attente des IRP, comme le pilote du contrôleur de disquette système, peut synchroniser la mise en file d’attente IRP sur un sémaphore, comme illustré dans la figure précédente :
Le thread appelle KeWaitForSingleObject avec un pointeur vers le stockage fourni par le pilote pour que l’objet sémaphore initialisé se mette en état d’attente.
Les IRP commencent à entrer qui nécessitent des opérations d’E/S de l’appareil. Les routines de dispatch du pilote insèrent chacune de ces IRP dans une file d’attente verrouillée sous le contrôle de verrouillage de rotation et appelez KeReleaseSemaphore avec un pointeur vers l’objet sémaphore, un renforcement de priorité déterminé par le pilote pour le thread (incrémenter, comme indiqué dans la figure précédente), un ajustement de 1 qui est ajouté au nombre du sémaphore lorsque chaque IRP est mis en file d’attente et une attente booléenne définie sur FALSE. Un nombre de sémaphores différent de zéro définit l’objet sémaphore à l’état Signaled, modifiant ainsi l’état du thread en attente sur Prêt.
Le noyau distribue le thread pour l’exécution dès qu’un processeur est disponible : autrement dit, aucun autre thread avec une priorité plus élevée n’est actuellement à l’état prêt et il n’existe aucune routine en mode noyau à exécuter à un IRQL plus élevé.
Le thread supprime un IRP de la file d’attente verrouillée sous le contrôle spin-lock, le transmet à d’autres routines de pilotes pour un traitement ultérieur, puis appelle à nouveau KeWaitForSingleObject . Si le sémaphore est toujours défini sur l’état Signaled (c’est-à-dire que son nombre reste différent de zéro, ce qui indique que d’autres IRP se trouvent dans la file d’attente verrouillée du pilote), le noyau modifie à nouveau l’état du thread d’attente à prêt.
En utilisant un sémaphore de comptage de cette manière, un thread de pilote « sait » qu’il y a un IRP à supprimer de la file d’attente verrouillée chaque fois que ce thread est exécuté.
Pour plus d’informations spécifiques à la gestion de l’IRQL lors de l’appel de KeReleaseSemaphore, consultez la section Remarques de KeReleaseSemaphore.
Toute routine de pilote standard qui s’exécute à un IRQL supérieur à PASSIVE_LEVEL ne peut pas attendre un intervalle différent de zéro sur les objets de répartiteur sans faire tomber le système ; Pour plus d’informations, consultez Kernel Dispatcher Objects . Toutefois, une telle routine peut appeler KeReleaseSemaphore lors de l’exécution à un IRQL inférieur ou égal à DISPATCH_LEVEL.
Pour obtenir un résumé des IRQL auxquelles les routines de pilotes standard s’exécutent, consultez Gestion des priorités matérielles. Pour connaître les exigences IRQL d’une routine de support spécifique, consultez la page de référence de la routine.