Définition et utilisation d’un objet d’événement

Tout pilote qui utilise un objet d’événement doit appeler KeInitializeEvent, IoCreateNotificationEvent ou IoCreateSynchronizationEvent avant d’attendre, définit, efface ou réinitialise l’événement. L’illustration suivante montre comment un pilote avec un thread peut utiliser un objet d’événement pour la synchronisation.

diagram illustrating waiting for an event object.

Comme le montre la figure précédente, un tel pilote doit fournir le stockage de l’objet d’événement, 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ôleur ou un pool non paginé alloué par le pilote.

Lorsque le pilote appelle KeInitializeEvent, il doit passer un pointeur vers le stockage résident du pilote pour l’objet d’événement. En outre, l’appelant doit spécifier l’état initial (signalé ou non signalé) pour l’objet d’événement. L’appelant doit également spécifier le type d’événement, qui peut être l’un des éléments suivants :

  • SynchronizationEvent

    Lorsqu’un événement de synchronisation est défini sur l’état Signaled, un thread unique qui attend la réinitialisation de l’événement sur Not-Signaled devient éligible à l’exécution et l’état de l’événement est automatiquement réinitialisé à Not-Signaled.

    Ce type d’événement est parfois appelé événement de déclearing automatique, car son état signalé est automatiquement réinitialisé chaque fois qu’une attente est satisfaite.

  • ÉvénmntNotification

    Lorsqu’un événement de notification est défini sur l’état Signaled, tous les threads qui attendaient que l’événement soit réinitialisé pour Not-Signaled deviennent éligibles à l’exécution et l’événement reste dans l’état Signaled jusqu’à ce qu’une réinitialisation explicite de Not-Signaled se produise : autrement dit, il existe un appel à KeClearEvent ou KeResetEvent avec le pointeur d’événement donné.

Peu de pilotes périphériques ou intermédiaires ont un thread dédié à un seul pilote, sans parler d’un ensemble de threads qui peuvent synchroniser leurs opérations en attendant un événement qui protège une ressource partagée.

La plupart des pilotes qui utilisent des objets d’événement pour attendre la fin d’une opération d’E/S définissent le type d’entrée sur NotificationEvent lorsqu’ils appellent KeInitializeEvent. Un objet d’événement configuré pour les irPs qu’un pilote crée avec IoBuildSynchronousFsdRequest ou IoBuildDeviceIoControlRequest est presque toujours initialisé en tant qu’événement NotificationEvent , car l’appelant attend l’événement pour la notification selon laquelle sa demande a été satisfaite par un ou plusieurs pilotes de niveau inférieur.

Une fois que le pilote s’est initialisé, son thread dédié au pilote, le cas échéant, et d’autres routines peuvent synchroniser leurs opérations sur l’événement. Par exemple, un pilote avec un thread qui gère la mise en file d’attente des irPs, tels que le pilote du contrôleur de disquette système, peut synchroniser le traitement IRP sur un événement, comme illustré dans la figure précédente :

  1. Le thread, qui a mis en file d’attente un IRP pour le traitement sur l’appareil, appelle KeWaitForSingleObject avec un pointeur vers le stockage fourni par le pilote pour l’objet d’événement initialisé.

  2. D’autres routines de pilote effectuent les opérations d’E/S nécessaires pour satisfaire l’IRP et, lorsque ces opérations sont terminées, la routine DpcForIsr du pilote appelle KeSetEvent avec un pointeur vers l’objet d’événement, un boost de priorité déterminé par le pilote pour le thread (Incrément, comme indiqué dans la figure précédente) et une attente booléenne définie sur FALSE. L’appel de KeSetEvent définit l’objet d’événement à l’état Signaled, ce qui modifie l’état du thread en attente comme 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 dont la priorité est plus élevée est actuellement dans l’état prêt et il n’existe aucune routine en mode noyau à exécuter à un runtime IRQL plus élevé.

    Le thread peut maintenant terminer l’IRP si le DpcForIsr n’a pas appelé IoCompleteRequest avec l’IRP déjà, et peut déjà mettre en file d’attente un autre IRP à traiter sur l’appareil.

L’appel de KeSetEvent avec le paramètre Wait défini sur TRUE indique l’intention de l’appelant d’appeler immédiatement une routine de prise en charge keWaitForSingleObjects ou KeWaitForMultipleObjects lors de son retour à partir de KeSetEvent.

Tenez compte des instructions suivantes pour définirtheWaitparametertoKeSetEvent :

Un thread paginable ou une routine de pilote paginable qui s’exécute sur IRQL < DISPATCH_LEVEL ne doit jamais appeler KeSetEvent avec le paramètre Wait défini sur TRUE. Un tel appel provoque une erreur de page irrécupérable si l’appelant est paginé entre les appels à KeSetEvent et KeWaitForSingleObject ou KeWaitForMultipleObjects.

Toute routine de pilote standard qui s’exécute à IRQL = DISPATCH_LEVEL ne peut pas attendre un intervalle différent de zéro sur les objets de répartiteur sans descendre le système. Toutefois, une telle routine peut appeler KeSetEvent lors de l’exécution à un runtime 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.

KeResetEvent retourne l’état précédent d’un événement donné : indique s’il a été défini sur Signaled ou non lorsque l’appel à KeResetEvent s’est produit. KeClearEvent définit simplement l’état de l’événement donné sur Not-Signaled.

Tenez compte des instructions suivantes pour appeler les routines de support précédentes :

Pour de meilleures performances, chaque pilote doit appeler KeClearEvent , sauf si l’appelant a besoin des informations retournées par KeResetEvent pour déterminer ce qu’il faut faire ensuite.