Ciclo de Vida do Buffer de Memória

O ciclo de vida de um buffer de memória abrange o tempo de quando o buffer é criado para quando ele é excluído. Este tópico descreve os cenários de uso do buffer e como eles afetam quando o buffer é excluído.

Na estrutura de driver do modo kernel (KMDF), um objeto de solicitação representa uma solicitação de E/S. Cada objeto de solicitação está associado a um ou mais objetos de memória e cada objeto de memória representa um buffer usado para entrada ou saída na solicitação.

Quando a estrutura cria objetos de solicitação e memória para representar uma solicitação de E/S de entrada, ela define o objeto de solicitação como o pai dos objetos de memória associados. Portanto, o objeto de memória não pode persistir mais do que o tempo de vida do objeto de solicitação. Quando o driver baseado em estrutura conclui a solicitação de E/S, a estrutura exclui o objeto de solicitação e o objeto de memória, de modo que os identificadores para esses dois objetos se tornam inválidos.

No entanto, o buffer subjacente é diferente. Dependendo de qual componente criou o buffer e como ele criou o buffer, o buffer pode ter uma contagem de referência e pode pertencer ao objeto de memória ou não. Se o objeto de memória possuir o buffer, o buffer terá uma contagem de referência e seu tempo de vida será limitado ao do objeto de memória. Se algum outro componente criou o buffer, os tempos de vida do buffer e do objeto de memória não estão relacionados.

Um driver baseado em estrutura também pode criar seus próprios objetos de solicitação para enviar para destinos de E/S. Uma solicitação criada pelo driver pode reutilizar um objeto de memória existente que o driver recebeu em uma solicitação de E/S. Um driver que frequentemente envia solicitações para destinos de E/S pode reutilizar os objetos de solicitação que ele cria.

Entender os tempos de vida do objeto de solicitação, o objeto de memória e o buffer subjacente é importante para garantir que o driver não tente fazer referência a um identificador ou ponteiro de buffer inválido.

Considere os seguintes cenários de uso:

Cenário 1: o driver recebe uma solicitação de E/S do KMDF, manipula-a e a conclui.

No cenário mais simples, o KMDF envia uma solicitação para o driver, que executa E/S e conclui a solicitação. Nesse caso, o buffer subjacente pode ter sido criado por um aplicativo de modo de usuário, por outro driver ou pelo próprio sistema operacional. Para obter informações sobre como acessar buffers, consulte Acessando buffers de dados em drivers de Framework-Based.

Quando o driver conclui a solicitação, a estrutura exclui o objeto de memória. Em seguida, o ponteiro do buffer é inválido.

Cenário 2: o driver recebe uma solicitação de E/S do KMDF e a encaminha para um destino de E/S.

Nesse cenário, o driver encaminha a solicitação para um destino de E/S. O código de exemplo a seguir mostra como um driver recupera um identificador para o objeto de memória de um objeto de solicitação de entrada, formata a solicitação para enviar para o destino de E/S e envia a solicitação:

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;

}

Quando o destino de E/S tiver concluído a solicitação, a estrutura chamará o retorno de chamada de conclusão que o driver definiu para a solicitação. O código a seguir mostra um retorno de chamada de conclusão simples:

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;

}

Quando o driver chama WdfRequestComplete de seu retorno de chamada de conclusão, a estrutura exclui o objeto de memória. O identificador do objeto de memória que o driver recuperou agora é inválido.

Cenário 3: o driver emite uma solicitação de E/S que usa um objeto de memória existente.

Alguns drivers emitem suas próprias solicitações de E/S e as enviam para destinos de E/S, que são representados por objetos de destino de E/S. O driver pode criar seu próprio objeto de solicitação ou reutilizar um objeto de solicitação criado pela estrutura. Usando qualquer técnica, um driver pode reutilizar um objeto de memória de uma solicitação anterior. O driver não deve alterar o buffer subjacente, mas pode passar um deslocamento de buffer quando formatar a nova solicitação de E/S.

Para obter informações sobre como formatar uma nova solicitação de E/S que usa um objeto de memória existente, consulte Enviando solicitações de E/S para destinos gerais de E/S.

Quando a estrutura formata a solicitação a ser enviada para o destino de E/S, ela faz uma referência no objeto de memória reciclada em nome do objeto de destino de E/S. O objeto de destino de E/S mantém essa referência até que uma das seguintes ações ocorra:

  • A solicitação foi concluída.
  • O driver reformata o objeto de solicitação novamente chamando um dos métodos WdfIoTargetFormatRequestXxx ouWdfIoTargetSendXxxSynchronously . Para obter mais informações sobre esses métodos, consulte Métodos de objeto de destino de E/S da Estrutura.
  • O driver chama WdfRequestReuse.

Quando a nova solicitação de E/S for concluída, a estrutura chamará o retorno de chamada de conclusão de E/S que o driver definiu para essa solicitação. Neste ponto, o objeto de destino de E/S ainda contém uma referência no objeto de memória. Portanto, no retorno de chamada de conclusão de E/S, o driver deve chamar WdfRequestReuse no objeto de solicitação criado pelo driver antes de concluir a solicitação original da qual recuperou o objeto de memória. Se o driver não chamar WdfRequestReuse, ocorrerá um bug marcar devido à referência extra.

Cenário 4: o driver emite uma solicitação de E/S que usa um novo objeto de memória.

A estrutura fornece três maneiras de os drivers criarem novos objetos de memória, dependendo da origem do buffer subjacente. Para obter mais informações, consulte Usando buffers de memória.

Se o buffer for alocado pela estrutura ou por meio de uma lista lookaside criada pelo driver, o objeto de memória será o proprietário do buffer, portanto, o ponteiro do buffer permanecerá válido enquanto o objeto de memória existir. Os drivers que emitem solicitações de E/S assíncronas sempre devem usar buffers pertencentes a objetos de memória para que a estrutura possa garantir que os buffers persistam até que a solicitação de E/S seja concluída novamente para o driver emissor.

Se o driver atribuir um buffer alocado anteriormente a um novo objeto de memória chamando WdfMemoryCreatePreallocated, o objeto de memória não será proprietário do buffer. Nesse caso, o tempo de vida do objeto de memória e o tempo de vida do buffer subjacente não estão relacionados. O driver deve gerenciar o tempo de vida do buffer e não deve tentar usar um ponteiro de buffer inválido.

Cenário 5: o driver reutiliza um objeto de solicitação que ele criou.

Um driver pode reutilizar os objetos de solicitação que ele cria, mas deve reinicializar cada objeto chamando WdfRequestReuse antes de cada reutilização. Para obter mais informações, consulte Reutilizando objetos de solicitação da estrutura.

Para obter um código de exemplo que reinicializa um objeto de solicitação, consulte os exemplos de Toaster e NdisEdge fornecidos com a versão KMDF.