Partager via


Utilisation de la synchronisation automatique

Presque tout le code d’un pilote basé sur le framework réside dans les fonctions de rappel d’événements. L’infrastructure synchronise automatiquement la plupart des fonctions de rappel d’un pilote, comme suit :

  • Le framework synchronise toujours les fonctions de rappel d'événements des objets d'appareil généraux, des objets d'appareil fonctionnels (FDO) et des objets d'appareil physiques (PDO) entre elles. Une seule des fonctions de rappel peut être appelée à la fois pour chaque appareil. L’exception est EvtDeviceSurpriseRemoval, EvtDeviceQueryRemoveet EvtDeviceQueryStop. Ces fonctions de rappel prennent en charge les événements Plug and Play (PnP) et de gestion de l'alimentation et sont appelées à IRQL = PASSIVE_LEVEL.

  • Si vous le souhaitez, l’infrastructure peut synchroniser l’exécution des fonctions de rappel qui gèrent les demandes d’E/S d’un pilote, afin que ces fonctions de rappel s’exécutent une par une. Plus précisément, l’infrastructure peut synchroniser les fonctions de rappel pour la file d’attente , l'interruption , l’appel de procédure différée (DPC) , le minuteur , l’élément de travail et les objets de fichier , ainsi que pour la fonction de rappel EvtRequestCancel de l’objet de requête. Le framework appelle la plupart de ces fonctions de rappel à IRQL = DISPATCH_LEVEL, mais vous pouvez forcer les fonctions de rappel de la file d'attente et de l'objet fichier à s'exécuter à IRQL = PASSIVE_LEVEL. (Les fonctions de rappel des éléments de travail sont toujours exécutées au NIVEAU PASSIF).

L’infrastructure implémente cette synchronisation automatique à l’aide d’un ensemble de verrous de synchronisation internes. L’infrastructure garantit que deux threads ou plus ne peuvent pas appeler la même fonction de rappel en même temps. Chaque thread doit attendre qu’il puisse acquérir un verrou de synchronisation avant d’appeler une fonction de rappel. (Si vous le souhaitez, les pilotes peuvent également acquérir ces verrous de synchronisation si nécessaire. Pour plus d’informations, consultez Using Framework Locks.)

Votre pilote devrait stocker des données spécifiques à l'objet dans espace de contexte de l'objet. Si votre pilote utilise uniquement des interfaces définies par l’infrastructure, seules les fonctions de rappel qui reçoivent un handle à l’objet peuvent accéder à ces données. Si l’infrastructure synchronise les appels aux fonctions de rappel du pilote, une seule fonction de rappel est appelée à la fois. L’espace de contexte de l’objet est accessible à une seule fonction de rappel à la fois.

À moins que votre pilote n'implémente une gestion des interruptions au niveau passif, le code qui gère les interruptions et accède aux données d'interruption doit s'exécuter au niveau IRQL (DIRQL) de l'appareil et nécessite une synchronisation supplémentaire. Pour plus d’informations, consultez synchronisation du code d’interruption.

Si votre pilote active la synchronisation automatique des fonctions de rappel qui gèrent les demandes d’E/S, l’infrastructure synchronise ces fonctions de rappel afin qu’elles s’exécutent une à la fois. Le tableau suivant répertorie les fonctions de rappel que l’infrastructure synchronise.

Type d’objet Fonctions de rappel synchronisées

Objet de la file d'attente

gestionnaires de requêtes, EvtIoQueueState, EvtIoResume, EvtIoStop

Objet du dossier

Toutes les fonctions de rappel

Objet de Requête

EvtRequestCancel

En option, le framework peut également synchroniser ces fonctions de rappel avec toutes les fonctions de rappel des objets d'interruption, DPC, work-item et timer que votre pilote fournit pour l'appareil (à l'exception de la fonction de rappel EvtInterruptIsr de l'objet d'interruption). Pour activer cette synchronisation supplémentaire, le pilote doit définir le membre AutomaticSerialization des structures de configuration de ces objets sur TRUE.

En résumé, la fonctionnalité de synchronisation automatique de l’infrastructure fournit les fonctionnalités suivantes :

  • L’infrastructure synchronise toujours les fonctions de rappel PnP et de gestion de l’alimentation de chaque appareil.

  • Si vous le souhaitez, l’infrastructure peut synchroniser les gestionnaires de demandes d’une file d’E/S et quelques fonctions de rappel supplémentaires (voir le tableau précédent).

  • Un pilote peut demander à l’infrastructure de synchroniser les fonctions de rappel pour les fonctions d'interruption, les DPC, les éléments de travail et les minuteurs.

  • Les pilotes doivent synchroniser le code qui gère les interruptions et accède aux données d'interruptions à l’aide des techniques décrites dans Synchronizing Interrupt Code.

  • Le framework ne synchronise pas les autres fonctions de rappel d'un pilote, telles que la fonction de rappel CompletionRoutine du pilote, ou les fonctions de rappel définies par l'objet cible d'E/S. Au lieu de cela, le framework fournit des verrous supplémentaires que les pilotes peuvent utiliser pour synchroniser ces fonctions de rappel.

Choix d’une étendue de synchronisation

Vous pouvez choisir de faire synchroniser l’infrastructure pour toutes les fonctions de rappel associées à toutes les files d’attente d’E/S d’un périphérique. Vous pouvez également choisir de laisser le framework synchroniser séparément les fonctions de rappel pour chaque file d’attente d’E/S d'un appareil. Les options de synchronisation disponibles pour votre pilote sont les suivantes :

  • Synchronisation au niveau de l’appareil

    L’infrastructure synchronise les fonctions de rappel pour toutes les files d’attente d’E/S de l’appareil, afin qu’elles s’exécutent une par une. Le cadre effectue cette synchronisation en acquérant le verrou de synchronisation de l'appareil avant d'appeler une fonction de rappel.

  • Synchronisation au niveau de la file d’attente

    L’infrastructure synchronise les fonctions de rappel pour chaque file d’attente d’E/S individuelle, afin qu’elles s’exécutent une par une. L’infrastructure effectue cette synchronisation en acquérant le verrou de synchronisation de la file d’attente avant d’appeler une fonction de rappel.

  • Aucune synchronisation

    L’infrastructure ne synchronise pas l’exécution des fonctions de rappel dans le tableau précédent et n’acquiert pas de verrou de synchronisation avant d’appeler les fonctions de rappel. Si la synchronisation est requise, le pilote doit le fournir.

Pour spécifier si vous souhaitez que l’infrastructure fournisse une synchronisation au niveau de l’appareil, une synchronisation au niveau de la file d’attente ou aucune synchronisation pour votre pilote, spécifiez une étendue de synchronisation pour votre objet pilote, les objets d’appareil ou les objets de file d’attente. Le SynchronizationScope membre de la structure WDF_OBJECT_ATTRIBUTES d’un objet identifie l’étendue de synchronisation de l’objet. Les valeurs d’étendue de synchronisation que votre pilote peut spécifier sont les suivantes :

WdfSynchronizationScopeDevice
Le cadre se synchronise en obtenant le verrou de synchronisation d’un objet d’appareil.

WdfSynchronizationScopeQueue
Le cadre se synchronise en obtenant le verrou de synchronisation d’un objet de file d’attente.

WdfSynchronizationScopeNone
L’infrastructure ne se synchronise pas et n’obtient pas de verrou de synchronisation.

WdfSynchronizationScopeInheritFromParent
Le framework obtient la valeur de SynchronizationScope de l'objet à partir de l'objet parent de l'objet.

En général, évitez d’utiliser la synchronisation au niveau de l’appareil.

Pour plus d’informations sur les valeurs d’étendue de synchronisation, consultez WDF_SYNCHRONIZATION_SCOPE.

L'étendue de la synchronisation par défaut pour les objets pilotes est WdfSynchronizationScopeNone. L'étendue de synchronisation par défaut pour les appareils et les files d'attente est WdfSynchronizationScopeInheritFromParent.

Pour utiliser le framework afin de fournir une synchronisation au niveau de l'appareil pour tous les appareils, définissez SynchronizationScope sur WdfSynchronizationScopeDevice dans la structure WDF_OBJECT_ATTRIBUTES de l'objet du pilote. Utilisez la valeur par défaut de WdfSynchronizationScopeInheritFromParent pour chaque objet d'appareil.

Pour fournir une synchronisation au niveau de l’appareil pour des appareils individuels, utilisez la valeur par défaut WdfSynchronizationScopeNone pour le pilote objet. Attribuez la valeur WdfSynchronizationScopeDevice à SynchronizationScope dans la structure WDF_OBJECT_ATTRIBUTES des différents objets d'appareil.

Si vous souhaitez que l’infrastructure fournisse une synchronisation au niveau de la file d’attente pour un appareil, vous pouvez utiliser les techniques suivantes :

  • Pour les versions 1.9 et ultérieures de framework, activez la synchronisation au niveau de la file d’attente pour les files d’attente individuelles en définissant WdfSynchronizationScopeQueue dans la structure WDF_OBJECT_ATTRIBUTES de l’objet file d’attente. Cette technique est préférée.

  • Vous pouvez également utiliser les étapes suivantes dans toutes les versions du framework :

    1. Attribuez la valeur WdfSynchronizationScopeQueue à SynchronizationScope dans la structure WDF_OBJECT_ATTRIBUTES de l'objet appareil.
    2. Utilisez la valeur par défaut de WdfSynchronizationScopeInheritFromParent pour les objets de file d'attente de chaque appareil.

Si vous ne souhaitez pas que l’infrastructure synchronise les fonctions de rappel qui gèrent les demandes d’E/S de votre pilote, utilisez la valeur par défaut SynchronizationScope pour les objets de pilote, de périphérique et de file d’attente de votre pilote. Dans ce cas, l’infrastructure ne synchronise pas automatiquement les fonctions de rappel liées aux E/S du pilote. Le framework peut appeler les fonctions de rappel à IRQL <= DISPATCH_LEVEL.

La définition d’une valeur SynchronizationScope synchronise uniquement les fonctions de rappel que contient le tableau précédent. Si vous souhaitez que le framework synchronise également les fonctions de rappel des objets d'interruption, DPC, work-item et timer du pilote, attribuez la valeur TRUE au membre AutomaticSerialization des structures de configuration de ces objets.

Toutefois, vous pouvez définir AutomaticSerialization sur TRUE uniquement si toutes les fonctions de rappel que vous souhaitez synchroniser s’exécutent au même IRQL. Le choix d’un niveau d’exécution , décrit ensuite, peut entraîner des niveaux IRQL incompatibles. Dans une telle situation, le pilote doit utiliser les verrous du framework au lieu d'activer AutomaticSerialization. Pour plus d’informations sur les structures de configuration pour les objets d’interruption, DPC, work-item et minuteur, et pour plus d’informations sur les restrictions qui s’appliquent à la définition de AutomaticSerialization dans ces structures, consultez WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIGet WDF_TIMER_CONFIG.

Si vous définissez AutomaticSerialization sur TRUE, sélectionnez la synchronisation au niveau de la file d’attente.

Choix d’un niveau d’exécution

Lorsqu’un pilote crée certains types d’objets framework, il peut spécifier un niveau d’exécution pour l’objet. Le niveau d'exécution spécifie l'IRQL auquel le framework appelle les fonctions de rappel d'événement de l'objet qui gèrent les requêtes d'E/S d'un pilote.

Si un pilote fournit un niveau d’exécution, le niveau fourni affecte les fonctions de rappel pour les objets de file d’attente et de fichier. En règle générale, si le pilote utilise la synchronisation automatique, l’infrastructure appelle ces fonctions de rappel à IRQL = DISPATCH_LEVEL. En spécifiant un niveau d'exécution, le pilote peut forcer le framework à appeler ces fonctions de rappel à IRQL = PASSIVE_LEVEL. L’infrastructure utilise les règles suivantes lors de la définition du runtime d’intégration au niveau duquel il appelle les fonctions de rappel d’objet file d’attente et de fichier :

  • Si un pilote utilise la synchronisation automatique, le framework appelle ses fonctions de rappel des objets de file d’attente et de fichier à IRQL = DISPATCH_LEVEL, sauf si le pilote demande au framework d’appeler ses fonctions de rappel à IRQL = PASSIVE_LEVEL.

  • Si un pilote n’utilise pas la synchronisation automatique et ne spécifie pas de niveau d’exécution, le framework peut appeler les fonctions de rappel associées à la file d'attente et à l'objet de fichier du pilote à IRQL <= DISPATCH_LEVEL.

Si votre pilote fournit des fonctions de rappel d’objet de fichier, vous souhaitez probablement que l’infrastructure appelle ces fonctions de rappel à IRQL = PASSIVE_LEVEL, car certaines données de fichier, telles que le nom de fichier, sont paginables.

Pour fournir un niveau d’exécution, votre pilote doit spécifier une valeur pour le membre ExecutionLevel de la structure WDF_OBJECT_ATTRIBUTES d’un objet. Les valeurs de niveau d’exécution que votre pilote peut spécifier sont les suivantes :

WdfExecutionLevelPassive
Le framework appelle les fonctions de rappel de l'objet à IRQL = PASSIVE_LEVEL.

WdfExecutionLevelDispatch
Le framework peut appeler les fonctions de rappel de l'objet à IRQL <= DISPATCH_LEVEL. Si le pilote utilise la synchronisation automatique, le framework appelle toujours les fonctions de rappel à IRQL = DISPATCH_LEVEL.

WdfExecutionLevelInheritFromParent
Le framework obtient la valeur ExecutionLevel de l'objet à partir du parent de l'objet.

Le niveau d’exécution par défaut pour les objets de pilote est WdfExecutionLevelDispatch. Le niveau d’exécution par défaut pour tous les autres objets est WdfExecutionLevelInheritFromParent.

Pour plus d’informations sur les valeurs de niveau d’exécution, consultez WDF_EXECUTION_LEVEL.

Le tableau suivant montre le niveau IRQL auquel l’infrastructure peut appeler les fonctions de rappel d’un pilote pour les objets de file d’attente et les objets de fichier.

Étendue de synchronisation Niveau d’exécution IRQL des fonctions de rappel des files d'attente et des fichiers

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= NIVEAU_DISPATCH

Vous pouvez définir le niveau d’exécution sur WdfExecutionLevelPassive ou WdfExecutionLevelDispatch pour le pilote, le périphérique, le fichier, la file d’attente, le minuteur et les objets généraux. Pour d’autres objets, vous pouvez uniquement définir WdfExecutionLevelInheritFromParent.

Spécifiez WdfExecutionLevelPassive si :

  • Les fonctions de rappel de votre pilote doivent appeler des méthodes du framework ou des routines du modèle de pilote Windows (WDM) que vous ne pouvez appeler qu'à IRQL = PASSIVE_LEVEL.

  • Les fonctions de rappel de votre pilote doivent accéder au code ou aux données paginables. Par exemple, les fonctions de rappel d’objet de fichier accèdent généralement aux données paginables.

Au lieu de définir WdfExecutionLevelPassive, votre pilote peut définir WdfExecutionLevelDispatch et fournir une fonction de rappel qui crée des éléments de travail s'il doit traiter certaines opérations à IRQL = PASSIVE_LEVEL.

Avant de décider si votre pilote doit définir le niveau d’exécution d’un objet sur WdfExecutionLevelPassive, déterminez l’irQL auquel votre pilote et d’autres pilotes dans la pile des pilotes sont appelés. Tenez compte des situations suivantes :

  • Si votre pilote se trouve en haut de la pile des pilotes en mode noyau, le système appelle généralement le pilote lorsque IRQL = PASSIVE_LEVEL. Le client d’un tel pilote peut être un pilote basé sur UMDF ou une application en mode utilisateur. La spécification de WdfExecutionLevelPassive n'affecte pas négativement les performances du pilote, car le framework n'a pas à mettre en file d'attente les appels de votre pilote aux éléments de travail qui sont appelés à IRQL = PASSIVE_LEVEL.

  • Si votre pilote n'est pas en haut de la pile, le système ne l'appellera probablement pas à IRQL = PASSIVE_LEVEL. Par conséquent, le framework doit mettre en file d'attente les appels de votre pilote aux éléments de travail, qui sont ensuite appelés à IRQL = PASSIVE_LEVEL. Ce processus peut entraîner des performances médiocres pour le pilote, par rapport à la possibilité d'appeler les fonctions de rappel de votre pilote à IRQL <= DISPATCH_LEVEL.

Pour les objets DPC et les objets timer qui ne représentent pas des timers de niveau passif, vous ne pouvez pas définir le membre AutomaticSerialization de la structure de configuration sur TRUE si vous définissez le niveau d'exécution de l'appareil parent sur WdfExecutionLevelPassive. Le framework acquiert les verrous de synchronisation du callback de l'objet appareil à IRQL = PASSIVE_LEVEL. Par conséquent, il ne peut pas utiliser les verrous pour synchroniser les fonctions de rappel des objets DPC ou retardateur, qui doivent s'exécuter à IRQL = DISPATCH_LEVEL. Dans ce cas, votre pilote doit utiliser des verrous de spin framework dans toutes les fonctions de rappel d'appareil, de DPC ou d'objet retardateur qui doivent être synchronisées les unes avec les autres.

Notez également que pour les objets timer qui représentent des timers de niveau passif, vous pouvez définir le membre AutomaticSerialization de la structure de configuration à TRUE uniquement si le niveau d'exécution de l'appareil parent est défini à WdfExecutionLevelPassive.