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, de définir, d’effacer ou de réinitialiser l’événement. La figure suivante montre comment un pilote avec un thread peut utiliser un objet d’événement pour la synchronisation.

diagramme illustrant l’attente d’un objet d’événement.

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 d’appareil d’un objet d’appareil créé par le pilote, l’extension de contrôleur s’il 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 événement. En outre, l’appelant doit spécifier l’état initial (signalé ou non) de 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 à l’état Signaled, un thread unique qui attend que l’événement soit réinitialisé à 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 contrôle automatique, car son état Signaled 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é à Not-Signaled deviennent éligibles à l’exécution et l’événement reste à l’état Signaled jusqu’à ce qu’une réinitialisation explicite de Not-Signaled se produise : autrement dit, il y a un appel à KeClearEvent ou KeResetEvent avec le pointeur d’événement donné.

Peu de pilotes d’appareil ou intermédiaires ont un seul thread dédié aux pilotes, et encore moins 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 NotificationEventlorsqu’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 que NotificationEvent , car l’appelant attend l’événement pour la notification que 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 IRP, comme 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 supprimé 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, une fois ces opérations terminées, la routine DpcForIsr du pilote appelle KeSetEvent avec un pointeur vers l’objet d’événement, une augmentation de priorité déterminée par le pilote pour le thread (incrémenter, comme illustré 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 sur 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 ayant une priorité plus élevée n’est actuellement à l’état prêt et il n’y a aucune routine en mode noyau à exécuter à un IRQL plus élevé.

    Le thread peut maintenant terminer l’IRP si le DpcForIsr n’a pas déjà appelé IoCompleteRequest avec l’IRP, et peut 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 KeWaitForSingleObject ou KeWaitForMultipleObjects au retour de KeSetEvent.

Tenez compte des instructions suivantes pour définir le paramètre WaitsurKeSetEvent :

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 se trouve être paginé entre les appels à KeSetEvent et KeWaitForSingleObjects 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 faire tomber le système. Toutefois, une telle routine peut appeler KeSetEvent lors de l’exécution d’un IRQL inférieur ou égal à DISPATCH_LEVEL.

Pour obtenir un résumé des IRQL auxquels 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é : 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 recommandations suivantes concernant le moment d’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.