Share via


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 al eliminar 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 puede conservarse 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. Según el componente que haya creado 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 enviarlos 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 las duraciones del objeto de solicitud, el objeto de memoria y el búfer subyacente es importante para asegurarse de que el controlador no intenta hacer referencia a un identificador o puntero de búfer no válidos.

Considere los escenarios de uso siguientes:

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 los 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 no es vá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 llama a la devolución de llamada de finalización que el controlador estableció para la solicitud. El código siguiente muestra una devolución de llamada de finalización 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;

}

Cuando el controlador llama a WdfRequestComplete desde su devolución de llamada de finalización, el marco elimina el objeto de memoria. El identificador de objeto de memoria que el controlador recuperado 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 destinos de E/S, que se representan mediante objetos de destino 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 dos 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 del 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 el marco da formato a la solicitud para enviar al destino de E/S, toma una referencia en el objeto de memoria reciclada en nombre del objeto de destino de E/S. El objeto de destino de E/S conserva esta referencia hasta que se produzca una de las siguientes acciones:

  • La solicitud se ha completado.
  • 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 objeto 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 llamar a WdfRequestReuse en el objeto de solicitud creado por el controlador antes de completar la solicitud original desde 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 para 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 marco asigna el búfer o desde una lista de lookaside creada por el controlador, el objeto de memoria posee el búfer, por lo que el puntero del búfer permanece válido siempre y cuando 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 garantizar que los búferes se conserven 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 administrar la duración 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 toaster y NdisEdge que se proporcionan con la versión de KMDF.