Compartir a través de


Ciclo de vida del búfer de memoria

El ciclo de vida de un búfer de memoria abarca el tiempo desde el momento en que se crea el búfer hasta cuando se elimina. En este tema se describen los escenarios de uso del búfer y cómo afectan cuando se elimina el búfer.

En el marco del controlador en modo kernel (KMDF), un objeto de solicitud representa una solicitud de E/S. Cada objeto de solicitud está asociado a uno o varios objetos de memoria, y cada objeto de memoria representa un búfer que se usa para la entrada o salida en la solicitud.

Cuando el marco crea objetos de solicitud y memoria para representar una solicitud de E/S entrante, establece el objeto de solicitud como elemento primario de los objetos de memoria asociados. Por lo tanto, el objeto de memoria no puede persistir más de la duración del objeto de solicitud. Cuando el controlador basado en marco completa la solicitud de E/S, el marco elimina el objeto de solicitud y el objeto de memoria, por lo que los identificadores de estos dos objetos no son válidos.

Sin embargo, el búfer subyacente es diferente. En función del componente que creó el búfer y cómo creó el búfer, es posible que el búfer tenga un recuento de referencias y que sea propiedad del objeto de memoria o que no lo sea. Si el objeto de memoria posee el búfer, el búfer tiene un recuento de referencias y su duración se limita a la del objeto de memoria. Si algún otro componente creó el búfer, las duraciones del búfer y el objeto de memoria no están relacionados.

Un controlador basado en marcos también puede crear sus propios objetos de solicitud para enviar a destinos de E/S. Una solicitud creada por el controlador puede reutilizar un objeto de memoria existente que el controlador recibió en una solicitud de E/S. Un controlador que envía con frecuencia solicitudes a destinos de E/S puede reutilizar los objetos de solicitud que crea.

Comprender los tiempos de vida del objeto de solicitud, el objeto de memoria y el buffer subyacente es importante para asegurarse de que el controlador no intente hacer referencia a un identificador o puntero de buffer no válidos.

Tenga en cuenta los siguientes escenarios de uso:

Escenario 1: el controlador recibe una solicitud de E/S de KMDF, la controla y la completa.

En el escenario más sencillo, KMDF envía una solicitud al controlador, que realiza E/S y completa la solicitud. En este caso, el búfer subyacente podría haber sido creado por una aplicación en modo de usuario, por otro controlador o por el propio sistema operativo. Para obtener información sobre cómo acceder a los búferes, consulte Acceso a búferes de datos en controladores de Framework-Based.

Cuando el controlador completa la solicitud, el marco elimina el objeto de memoria. El puntero del búfer es entonces inválido.

Escenario 2: el controlador recibe una solicitud de E/S de KMDF y la reenvía a un destino de E/S.

En este escenario, el controlador reenvía la solicitud a un destino de E/S. En el código de ejemplo siguiente se muestra cómo un controlador recupera un identificador para el objeto de memoria de un objeto de solicitud entrante, da formato a la solicitud para enviar al destino de E/S y envía la solicitud:

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;

}

Cuando el destino de E/S ha completado la solicitud, el marco de trabajo llama al callback de finalización que el controlador estableció para la solicitud. En el código siguiente se muestra una función de retorno de finalización sencilla.

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;

}

Cuando el controlador llama a WdfRequestComplete desde su devolución de llamada de finalización, el framework elimina el objeto de memoria. El identificador de objeto de memoria que recuperó el controlador ahora no es válido.

Escenario 3: el controlador emite una solicitud de E/S que usa un objeto de memoria existente.

Algunos controladores emiten sus propias solicitudes de E/S y las envían a objetivos de E/S, que se representan mediante objetos de E/S. El controlador puede crear su propio objeto de solicitud o reutilizar un objeto de solicitud creado por el marco. Con cualquiera de las técnicas, un controlador puede reutilizar un objeto de memoria de una solicitud anterior. El controlador no debe cambiar el búfer subyacente, pero puede pasar un desplazamiento de búfer cuando da formato a la nueva solicitud de E/S.

Para obtener información sobre cómo dar formato a una nueva solicitud de E/S que usa un objeto de memoria existente, consulte Envío de solicitudes de E/S a destinos de E/S generales.

Cuando la plataforma da formato a la solicitud para enviar al destino de entrada/salida, obtiene una referencia del objeto de memoria reciclada en representación del objeto de destino de E/S. El objeto de destino de E/S conserva esta referencia hasta que se produce una de las siguientes acciones:

  • Se ha completado la solicitud.
  • El controlador vuelve a formatear el objeto de solicitud llamando a uno de los métodos WdfIoTargetFormatRequestXxx o WdfIoTargetSendXxxSynchronously . Para obtener más información sobre estos métodos, vea Métodos de objetos de destino de E/S de marco.
  • El controlador llama a WdfRequestReuse.

Una vez completada la nueva solicitud de E/S, el marco llama a la devolución de llamada de finalización de E/S establecida por el controlador para esta solicitud. En este momento, el objeto de destino de E/S sigue manteniendo una referencia en el objeto de memoria. Por lo tanto, en la devolución de llamada de finalización de E/S, el controlador debe invocar WdfRequestReuse en el objeto de solicitud creado por el controlador antes de completar la solicitud original de la que recuperó el objeto de memoria. Si el controlador no llama a WdfRequestReuse, se produce una comprobación de errores debido a la referencia adicional.

Escenario 4: el controlador emite una solicitud de E/S que usa un nuevo objeto de memoria.

El marco proporciona tres maneras de que los controladores creen nuevos objetos de memoria, en función del origen del búfer subyacente. Para obtener más información, consulte Uso de búferes de memoria.

Si el framework asigna el búfer o si proviene de una lista lookaside creada por el controlador, el objeto de memoria posee el búfer, de modo que el puntero del búfer permanece válido mientras exista el objeto de memoria. Los controladores que emiten solicitudes de E/S asincrónicas siempre deben usar búferes que pertenecen a objetos de memoria para que el marco pueda asegurarse de que los búferes se conservan hasta que la solicitud de E/S se haya completado de nuevo en el controlador emisor.

Si el controlador asigna un búfer asignado previamente a un nuevo objeto de memoria llamando a WdfMemoryCreatePreallocated, el objeto de memoria no posee el búfer. En este caso, la duración del objeto de memoria y la duración del búfer subyacente no están relacionadas. El controlador debe gestionar la vida útil del búfer y no debe intentar usar un puntero de búfer no válido.

Escenario 5: El controlador reutiliza un objeto de solicitud que creó.

Un controlador puede reutilizar los objetos de solicitud que crea, pero debe reinicializar cada objeto llamando a WdfRequestReuse antes de cada reutilización. Para obtener más información, consulte Reutilización de objetos de solicitud de marco.

Para obtener código de ejemplo que reinicializa un objeto de solicitud, consulte los ejemplos de Toaster y NdisEdge que se proporcionan con la versión de KMDF.