Cycle de vie de la mémoire tampon

Le cycle de vie d’une mémoire tampon s’étend sur la durée de création de la mémoire tampon jusqu’à sa suppression. Cette rubrique décrit les scénarios d’utilisation de la mémoire tampon et leur impact sur la suppression de la mémoire tampon.

Dans l’infrastructure de pilote en mode noyau (KMDF), un objet de requête représente une demande d’E/S. Chaque objet de requête est associé à un ou plusieurs objets mémoire, et chaque objet mémoire représente une mémoire tampon utilisée pour l’entrée ou la sortie dans la requête.

Lorsque l’infrastructure crée des objets de requête et de mémoire pour représenter une demande d’E/S entrante, elle définit l’objet de requête comme parent des objets de mémoire associés. Par conséquent, l’objet mémoire ne peut pas persister plus longtemps que la durée de vie de l’objet de requête. Lorsque le pilote basé sur l’infrastructure termine la demande d’E/S, l’infrastructure supprime l’objet de requête et l’objet mémoire, de sorte que les handles de ces deux objets ne deviennent pas valides.

Toutefois, la mémoire tampon sous-jacente est différente. Selon le composant qui a créé la mémoire tampon et la façon dont il a créé la mémoire tampon, la mémoire tampon peut avoir un nombre de références et peut appartenir à l’objet mémoire, ou non. Si l’objet mémoire est propriétaire de la mémoire tampon, la mémoire tampon a un nombre de références et sa durée de vie est limitée à celle de l’objet mémoire. Si un autre composant a créé la mémoire tampon, les durées de vie de la mémoire tampon et de l’objet mémoire ne sont pas liées.

Un pilote basé sur l’infrastructure peut également créer ses propres objets de requête à envoyer aux cibles d’E/S. Une requête créée par un pilote peut réutiliser un objet mémoire existant que le pilote a reçu dans une demande d’E/S. Un pilote qui envoie fréquemment des requêtes aux cibles d’E/S peut réutiliser les objets de requête qu’il crée.

Il est important de comprendre la durée de vie de l’objet de requête, de l’objet mémoire et de la mémoire tampon sous-jacente pour vous assurer que votre pilote ne tente pas de référencer un handle ou un pointeur de mémoire tampon non valide.

Envisagez les scénarios d'utilisation suivants :

Scénario 1 : Le pilote reçoit une demande d’E/S de KMDF, la gère et la termine.

Dans le scénario le plus simple, KMDF distribue une demande au pilote, qui effectue des E/S et termine la requête. Dans ce cas, la mémoire tampon sous-jacente peut avoir été créée par une application en mode utilisateur, par un autre pilote ou par le système d’exploitation lui-même. Pour plus d’informations sur l’accès aux mémoires tampons, consultez Accès aux mémoires tampons de données dans les pilotes Framework-Based.

Lorsque le pilote termine la requête, l’infrastructure supprime l’objet mémoire. Le pointeur de la mémoire tampon n’est pas valide.

Scénario 2 : Le pilote reçoit une requête d’E/S de KMDF et la transfère à une cible d’E/S.

Dans ce scénario, le pilote transfère la demande à une cible d’E/S. L’exemple de code suivant montre comment un pilote récupère un handle à l’objet mémoire à partir d’un objet de requête entrant, met en forme la demande à envoyer à la cible d’E/S et envoie la requête :

VOID
EvtIoRead(
    IN WDFQUEUE Queue,
    IN WDFREQUEST Request,
    IN size_t Length
    )
{
    NTSTATUS status;
    WDFMEMORY memory;
    WDFIOTARGET ioTarget;
    BOOLEAN ret;
    ioTarget = WdfDeviceGetIoTarget(WdfIoQueueGetDevice(Queue));

    status = WdfRequestRetrieveOutputMemory(Request, &memory);
    if (!NT_SUCCESS(status)) {
        goto End;
    }

    status = WdfIoTargetFormatRequestForRead(ioTarget,
                                    Request,
                                    memory,
                                    NULL,
                                    NULL);
    if (!NT_SUCCESS(status)) {
        goto End;
    }

    WdfRequestSetCompletionRoutine(Request,
                                    RequestCompletionRoutine,
                                    WDF_NO_CONTEXT);

    ret = WdfRequestSend (Request, ioTarget, WDF_NO_SEND_OPTIONS);
    if (!ret) {
        status = WdfRequestGetStatus (Request);
        goto End;
    }

    return;

End:
    WdfRequestComplete(Request, status);
    return;

}

Lorsque la cible d’E/S a terminé la demande, l’infrastructure appelle le rappel d’achèvement défini par le pilote pour la requête. Le code suivant montre un rappel d’achèvement simple :

VOID
RequestCompletionRoutine(
    IN WDFREQUEST                  Request,
    IN WDFIOTARGET                 Target,
    PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
    IN WDFCONTEXT                  Context
    )
{
    UNREFERENCED_PARAMETER(Target);
    UNREFERENCED_PARAMETER(Context);

    WdfRequestComplete(Request, CompletionParams->IoStatus.Status);

    return;

}

Lorsque le pilote appelle WdfRequestComplete à partir de son rappel d’achèvement, l’infrastructure supprime l’objet mémoire. Le handle d’objet mémoire récupéré par le pilote n’est plus valide.

Scénario 3 : Le pilote émet une demande d’E/S qui utilise un objet mémoire existant.

Certains pilotes émettent leurs propres demandes d’E/S et les envoient aux cibles d’E/S, qui sont représentées par des objets cibles d’E/S. Le pilote peut créer son propre objet de requête ou réutiliser un objet de requête créé par l’infrastructure. À l’aide de l’une ou l’autre technique, un pilote peut réutiliser un objet mémoire d’une requête précédente. Le pilote ne doit pas modifier la mémoire tampon sous-jacente, mais il peut passer un décalage de mémoire tampon lorsqu’il met en forme la nouvelle demande d’E/S.

Pour plus d’informations sur la mise en forme d’une nouvelle demande d’E/S qui utilise un objet mémoire existant, consultez Envoi de demandes d’E/S à des cibles d’E/S générales.

Lorsque l’infrastructure met en forme la demande à envoyer à la cible d’E/S, elle prend une référence sur l’objet de mémoire recyclée pour le compte de l’objet cible d’E/S. L’objet cible d’E/S conserve cette référence jusqu’à ce que l’une des actions suivantes se produise :

  • La demande a été effectuée.
  • Le pilote reforma l’objet de requête en appelant l’une des méthodes WdfIoTargetFormatRequestXxx ou WdfIoTargetSendXxxSynchronously . Pour plus d’informations sur ces méthodes, consultez Méthodes d’objet cible d’E/S framework.
  • Le pilote appelle WdfRequestReuse.

Une fois la nouvelle demande d’E/S terminée, l’infrastructure appelle le rappel d’achèvement d’E/S défini par le pilote pour cette requête. À ce stade, l’objet cible d’E/S contient toujours une référence sur l’objet mémoire. Par conséquent, dans le rappel d’achèvement des E/S, le pilote doit appeler WdfRequestReuse sur l’objet de requête créé par le pilote avant de terminer la requête d’origine à partir de laquelle il a récupéré l’objet mémoire. Si le pilote n’appelle pas WdfRequestReuse, un bogue case activée se produit en raison de la référence supplémentaire.

Scénario 4 : Le pilote émet une demande d’E/S qui utilise un nouvel objet mémoire.

Le framework fournit trois façons pour les pilotes de créer de nouveaux objets mémoire, en fonction de la source de la mémoire tampon sous-jacente. Pour plus d’informations, consultez Utilisation de mémoire tampons.

Si la mémoire tampon est allouée par l’infrastructure ou à partir d’une liste de lookaside créée par le pilote, l’objet mémoire est propriétaire de la mémoire tampon, de sorte que le pointeur de mémoire tampon reste valide tant que l’objet mémoire existe. Les pilotes qui émettent des demandes d’E/S asynchrones doivent toujours utiliser des mémoires tampons appartenant à des objets mémoire afin que l’infrastructure puisse s’assurer que les mémoires tampons sont conservées jusqu’à ce que la demande d’E/S soit retournée au pilote émetteur.

Si le pilote affecte une mémoire tampon précédemment allouée à un nouvel objet mémoire en appelant WdfMemoryCreatePreallocated, l’objet mémoire ne possède pas la mémoire tampon. Dans ce cas, la durée de vie de l’objet mémoire et la durée de vie de la mémoire tampon sous-jacente ne sont pas liées. Le pilote doit gérer la durée de vie de la mémoire tampon et ne doit pas tenter d’utiliser un pointeur de mémoire tampon non valide.

Scénario 5 : Le pilote réutilise un objet de requête qu’il a créé.

Un pilote peut réutiliser les objets de requête qu’il crée, mais il doit réinitialiser chacun de ces objets en appelant WdfRequestReuse avant chaque réutilisation. Pour plus d’informations, consultez Réutilisation d’objets de demande d’infrastructure.

Pour obtenir un exemple de code qui réinitialise un objet de requête, consultez les exemples Grille-pain et NdisEdge fournis avec la version KMDF.