Partager via


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 pas de demandes d’E/S en attente pour le pilote, et que les routines de répartition du pilote peuvent définir le sémaphore sur l’état Signaled juste après avoir mis en file d’attente un 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 répartition des 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 montre comment un pilote avec un thread peut utiliser un objet sémaphore.

diagramme illustrant l’attente d’un objet sémaphore.

Comme l’illustre 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 du contrôleur si elle utilise un objet contrôleurou un pool non paginé alloué par le pilote.

Lorsque la routine addDevice du pilote appelle KeInitializeSemaphore, il doit passer un pointeur vers le stockage résident du pilote pour l’objet sémaphore. En outre, l’appelant doit spécifier un Count pour l’objet sémaphore, comme indiqué 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 de = 1

    Lorsque ce sémaphore est défini sur l’état Signaled, un thread unique qui attend que le sémaphore soit défini sur l’état Signaled devient éligible à l’exécution et peut accéder à la 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 un accès exclusif à la ressource protégée par le sémaphore.

  • limite de > 1

    Lorsque ce sémaphore est défini sur l’état Signaled, un certain nombre de threads qui attendent que l’objet sémaphore soit défini sur l’état Signaled devienne éligible à l’exécution et peut accéder à la ressource protégée par le sémaphore.

    Ce type de sémaphore est appelé un sémaphore de comptage de , car la routine qui définit le sémaphore sur l’état Signaled spécifie également le nombre de threads en attente que leurs états peuvent changer d’attente à prêt. Le nombre de tels threads en attente peut être le Limite défini lorsque le sémaphore a été initialisé ou un certain nombre inférieur à ce Limite.

Peu de pilotes de périphérique ou intermédiaire ont un thread créé par un seul pilote ; même moins de threads peuvent attendre l’acquisition ou la libération d’un sémaphore. Peu de pilotes fournis par le système utilisent des objets sémaphores et, de ceux qui le font, même moins d’utiliser un sémaphore binaire. Bien qu’un sémaphore binaire semble être similaire dans les 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 dans 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é à l’appareil qui gère la mise en file d’attente des irPs, comme le pilote du contrôleur de floppy système, peut synchroniser la mise en file d’attente IRP sur un sémaphore, comme illustré dans la figure précédente :

  1. Le thread appelle KeWaitForSingleObject avec un pointeur vers le stockage fourni par le pilote pour que l’objet sémaphore initialisé se place dans un état d’attente.

  2. Les adresses IP virtuelles commencent à entrer qui nécessitent des opérations d’E/S d’appareil. Les routines de répartition du pilote insèrent chacun de ces IRP dans une file d’attente interblocée sous le contrôle spin-lock et appelez KeReleaseSemaphore avec un pointeur vers l’objet sémaphore, un boost de priorité déterminé par le pilote pour le thread (Incrémenter, comme indiqué dans la figure précédente), un Ajustement de 1 ajouté au nombre de sémaphores, car chaque IRP est mis en file d’attente, et un d’attente booléen défini sur FALSE. Un sémaphore différent de zéro définit l’objet sémaphore à l’état Signaled, ce qui modifie l’état du thread en attente pour qu’il soit prêt.

  3. Le noyau répartit le thread pour l’exécution dès qu’un processeur est disponible : autrement dit, aucun autre thread avec une priorité plus élevée est actuellement dans l’état prêt et il n’y a pas de routines en mode noyau à exécuter à un runtime IRQL plus élevé.

    Le thread supprime un IRP de la file d’attente interblocée sous le contrôle spin-lock, le transmet à d’autres routines de pilote pour un traitement plus approfondi et appelle KeWaitForSingleObject à nouveau. Si le sémaphore est toujours défini sur l’état Signaled (autrement dit, son nombre reste différent de zéro, ce qui indique qu’un plus grand nombre d’IRPs se trouvent dans la file d’attente interblocée du pilote), le noyau change à nouveau l’état du thread d’attendre pour être prêt.

    À l’aide d’un sémaphore de comptage de cette façon, un thread de pilote « sait » qu’il existe un IRP à supprimer de la file d’attente verrouillée chaque fois que ce thread est exécuté.

Pour plus d’informations sur la gestion de IRQL lors de l’appel de KeReleaseSemaphore, consultez la section Notes de KeReleaseSemaphore.

Toute routine de pilote standard qui s’exécute à un runtime IRQL supérieur à PASSIVE_LEVEL ne peut pas attendre un intervalle différent de zéro sur les objets de répartiteur sans réduire le système ; consultez objets de répartiteur de noyau pour plus d’informations. 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 sur lesquels 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.