Share via


Gestion des files d’attente verrouillées avec un thread Driver-Created

Les nouveaux pilotes doivent utiliser l’infrastructure de file d’attente cancel-safe IRP de préférence pour les méthodes décrites dans cette section.

Comme le pilote du contrôleur de disquette système, un pilote avec un thread dédié au périphérique, plutôt qu’une routine StartIo , gère généralement sa propre file d’attente des IRP dans une file d’attente doublement liée. Le thread du pilote extrait les irps de sa file d’attente verrouillée quand il y a du travail à effectuer sur l’appareil.

En général, le pilote doit gérer la synchronisation avec son thread avec toutes les ressources partagées entre le thread et d’autres routines de pilote. Le pilote doit également disposer d’un moyen d’avertir son thread créé par le pilote que les runtimes d’intégration sont mis en file d’attente. En règle générale, le thread attend un objet de répartiteur, stocké dans l’extension de périphérique, jusqu’à ce que les routines dispatch du pilote définissent l’objet répartiteur à l’état Signaled après l’insertion d’un IRP dans la file d’attente verrouillée.

Lorsque les routines dispatch du pilote sont appelées, chacune vérifie les paramètres dans l’emplacement de la pile d’E/S de l’IRP d’entrée et, si elles sont valides, met en file d’attente la demande pour un traitement ultérieur. Pour chaque IRP mis en file d’attente vers un thread dédié au pilote, la routine de répartition doit configurer le contexte dont son thread a besoin pour traiter cette IRP avant d’appeler ExInterlockedInsertXxxList. L’emplacement de la pile d’E/S du pilote dans chaque IRP permet au thread du pilote d’accéder à l’extension de périphérique de l’objet de périphérique cible, où le pilote peut partager des informations de contexte avec son thread, car le thread supprime chaque IRP de la file d’attente.

Un pilote qui met en file d’attente des IRP annulables doit implémenter une routine Annuler . Étant donné que les IRP sont annulés de façon asynchrone, vous devez vous assurer que votre pilote évite les conditions de course qui peuvent en résulter. Consultez Synchronisation de l’annulation des IRP Pour plus d’informations sur les conditions de concurrence associées à l’annulation des IRP et des techniques permettant de les éviter.

Tout thread créé par un pilote s’exécute à IRQL = PASSIVE_LEVEL et à une priorité d’exécution de base précédemment définie lorsque le pilote a appelé PsCreateSystemThread. L’appel du thread à ExInterlockedRemoveHeadList déclenche temporairement l’IRQL pour DISPATCH_LEVEL sur le processeur actuel pendant que l’IRP est supprimé de la file d’attente interne du pilote. L’IRQL d’origine est restauré en PASSIVE_LEVEL au retour de cet appel.

Tout thread de pilote (ou rappel de thread de travail fourni par le pilote) doit gérer avec soin les IRQL auxquels il s’exécute. Prenons l’exemple suivant :

  • Étant donné que les threads système s’exécutent généralement à IRQL = PASSIVE_LEVEL, il est possible qu’un thread de pilote attende que les objets de répartiteur définis par le noyau soient définis sur l’état signalé.

    Par exemple, un thread dédié à l’appareil peut attendre que d’autres pilotes répondent à un événement et effectuer un certain nombre d’IRP de transfert partiel que le thread configure avec IoBuildSynchronousFsdRequest.

  • Toutefois, un tel thread dédié à l’appareil doit déclencher l’IRQL sur le processeur actuel avant d’appeler certaines routines de support.

    Par exemple, si un pilote utilise DMA, son thread dédié à l’appareil doit imbriquer ses appels à AllocateAdapterChannel et FreeAdapterChannel entre les appels à KeRaiseIrql et KeLowerIrql , car ces routines et certaines autres routines de prise en charge pour les opérations DMA doivent être appelées à IRQL = DISPATCH_LEVEL.

    N’oubliez pas que les routines StartIo sont exécutées à DISPATCH_LEVEL, de sorte que les pilotes qui utilisent DMA n’ont pas besoin d’effectuer des appels aux routines KeXxxIrql à partir de leurs routines StartIo .

  • Un thread créé par un pilote peut accéder à la mémoire paginable, car il s’exécute dans un contexte de thread non linéaire (le sien) à IRQL = PASSIVE_LEVEL, mais de nombreuses autres routines de pilote standard s’exécutent à IRQL >= DISPATCH_LEVEL. Si un thread créé par un pilote alloue de la mémoire accessible par une telle routine, il doit allouer la mémoire à partir d’un pool non paginé. Par exemple, si un thread dédié à l’appareil alloue une mémoire tampon qui sera accessible ultérieurement par l’ISR du pilote ou SynchCritSection, AdapterControl, AdapterListControl, ControllerControl, DpcForIsr, CustomDpc, IoTimer, CustomTimerDpc ou, dans un pilote de niveau supérieur, la routine IoCompletion , la mémoire allouée par le thread ne peut pas être paginable.

  • Si le pilote conserve des informations d’état partagé ou des ressources dans une extension de périphérique, un thread de pilote (comme une routine StartIo ) doit synchroniser son accès à un appareil physique et aux données partagées avec les autres routines du pilote qui accèdent au même appareil, au même emplacement de mémoire ou aux mêmes ressources.

    Si le thread partage l’appareil ou l’état avec l’ISR, il doit utiliser KeSynchronizeExecution pour appeler une routine SynchCritSection fournie par le pilote afin de programmer l’appareil ou d’accéder à l’état partagé. Consultez Utilisation des sections critiques.

    Si le thread partage l’état ou les ressources avec des routines autres que l’ISR, le pilote doit protéger l’état partagé ou les ressources avec un verrou de rotation exécutif initialisé pour lequel le pilote fournit le stockage. Pour plus d’informations, consultez Verrous tournants.

Pour plus d’informations sur les compromis de conception d’un à l’aide d’un thread de pilote pour un appareil lent, consultez Interrogation d’un appareil. Voir aussi Gestion des priorités matérielles. Pour plus d’informations sur les IRQL pour des routines de support particulières, consultez la page de référence de la routine.