Partager via


Exemples de synchronisation

Les exemples suivants illustrent ce qu’un minidriver doit faire en ce qui concerne la synchronisation et inclut des exemples de cas où la synchronisation ne doit pas être utilisée :

  • Exemple 1 : Minidriver avec un ISR fonctionnel

    Si la synchronisation de classes de flux est activée, tous les points d’entrée minidriver sont appelés au niveau irQL déclenché, à l’aide de KeSynchronizeExecution, ce qui signifie que le niveau IRQ de l’adaptateur et tous les niveaux IRQ inférieurs sont masqués lorsque le minidriver exécute son code. Par conséquent, il est impératif que le minidriver n’effectue qu’une petite quantité de travail dans ce mode.

    Le minidriver ne doit pas exécuter de code qui prend généralement plus de 10 à 20 microsecondes au niveau du runtime IRQL élevé. Si vous utilisez la build de débogage de stream.sys, la classe de flux journalise le temps passé au runtime IRQL déclenché et affirme si le pilote passe trop de temps là-bas. Si le minidriver a simplement besoin de programmer des registres DMA matériels pour une demande ou doit simplement lire des ports dans son ISR, il est généralement acceptable d’effectuer tout son traitement à l’irQL déclenché.

    Si le minidriver doit effectuer un traitement qui prend plus de quelques microsecondes, par exemple un minidriver qui transfère des données via piO, le minidriver doit utiliser StreamClassCallAtNewPriority pour planifier un rappel DISPATCH_LEVEL. Dans le rappel, le minidriver peut prendre jusqu’à environ 1/2 à 1 milliseconde pour effectuer son traitement. Une chose à retenir quand dans ce mode est que le rappel DISPATCH_LEVEL n’est pas synchronisé avec l’ISR.

    Ce manque de synchronisation n’est pas un problème si le matériel reste stable lorsque le minidriver accède aux ressources (par exemple, les ports ou une file d’attente) pendant le rappel ainsi que dans l’ISR. Toutefois, si l’instabilité peut être un problème, le minidriver doit utiliser StreamClassCallAtNewPriority pour planifier un rappel haute priorité où le rappel DISPATCH_LEVEL touche les ressources partagées avec les ressources utilisées par l’ISR.

    Notez qu’un rappel haute priorité équivaut à appeler KeSynchronizeExecution. KeSynchronizeExecution nécessite que le minidriver référence plusieurs paramètres que StreamClassCallAtNewPriority ne fait pas, mais en général, les deux entraînent le même comportement.

    Si le minidriver n’a besoin que occasionnellement d’exécuter du code qui prend plus de 1/2 à 1 milliseconde, ou doit parfois appeler des services à PASSIVE_LEVEL (par exemple au moment de l’initialisation), la définition de StreamClassCallAtNewPriority sur LOW priority peut être utilisée pour acquérir un thread de travail PASSIVE_LEVEL. Notez qu’un rappel de faible priorité n’est pas synchronisé avec n’importe quoi et que le minidriver peut recevoir de nouvelles demandes (en supposant que le paramètre ReadyForNextRequest NotificationType est en attente) ou un appel ISR lors de l’exécution d’un rappel de faible priorité.

  • Exemple 2 : Minidriver sans ISR

    Si la synchronisation de classes de flux est activée, les points d’entrée du minidriver sont tous appelés à DISPATCH_LEVEL. Le minidriver peut effectuer le traitement d’environ 1/2 à 1 millisecondes sans avoir besoin d’ajuster la priorité. Si le minidriver n’a besoin que occasionnellement d’exécuter du code qui prend plus de 1/2 millisecondes, ou doit parfois appeler des services à PASSIVE_LEVEL (par exemple, au moment de l’initialisation), StreamClassCallAtNewPriority avec faible priorité peut être utilisé pour acquérir un thread de travail PASSIVE_LEVEL. Notez qu’un rappel de faible priorité n’est pas synchronisé avec n’importe quoi et que le minidriver peut recevoir de nouvelles demandes (en supposant que le paramètre ReadyForNextRequest NotificationType est en attente) lors de l’exécution d’un rappel de faible priorité.

  • Quand la synchronisation de classes de flux ne doit pas être utilisée

    Voici des exemples de situations où la synchronisation de classes de flux ne doit pas être utilisée. Il s’agit notamment des paramètres suivants :

    • Lorsque les pilotes fréquemment (plus de 20 % des demandes reçues par le minidriver) doivent effectuer le traitement qui prend plus de 1 milliseconde ou doivent appeler fréquemment des services PASSIVE_LEVEL, tels que les services Microsoft DirectDraw. Lorsque vous utilisez la version de débogage de stream.sys, la classe de flux affirme ces deux cas et s’arrête si elles sont détectées avec la synchronisation activée.

    • Lorsque le minidriver est un filtre sans matériel associé. Un tel minidriver doit s’exécuter à PASSIVE_LEVEL, car il n’existe aucun matériel sous-jacent à synchroniser avec et le minidriver effectue généralement beaucoup de traitement. Il est plus facile d’effectuer votre propre synchronisation dans ce cas que de gaspiller la surcharge à l’aide de la synchronisation de classes de flux. Si nécessaire, utilisez des mutex pour protéger vos files d’attente.

      Les bogues dans le code de synchronisation peuvent souvent être difficiles à trouver et, dans certains environnements (tels que les systèmes d’exploitation basés sur NT s’exécutant sur des systèmes multiprocesseurs), les bogues peuvent apparaître uniquement après de nombreuses heures de stress. En fonction de l’expérience des fournisseurs, ce ne sont pas les types d’éléments que la plupart des fournisseurs ont la capacité ou le désir de déboguer. Seuls les enregistreurs de pilotes familiarisés avec l’écriture de pilotes de périphérique WDM entièrement asynchrones doivent tenter d’effectuer leur propre synchronisation.

    • Lorsque le minidriver est un pilote de type bus-on-bus (par exemple, un pilote périphérique USB ou 1394) qui ne s’inquiète pas vraiment de la synchronisation du matériel réel, mais appelle simplement les demandes jusqu’à la couche suivante à PASSIVE_LEVEL et reçoit généralement des rappels à DISPATCH_LEVEL.