Condividi tramite


Ciclo di vita del buffer di memoria

Il ciclo di vita di un buffer di memoria si estende nel tempo da quando viene creato il buffer al momento dell'eliminazione. Questo argomento descrive gli scenari di utilizzo del buffer e il modo in cui influiscono quando il buffer viene eliminato.

Nel framework driver in modalità kernel (KMDF), un oggetto request rappresenta una richiesta di I/O. Ogni oggetto richiesta è associato a uno o più oggetti di memoria e ogni oggetto di memoria rappresenta un buffer utilizzato per l'input o l'output nella richiesta.

Quando il framework crea oggetti di richiesta e memoria per rappresentare una richiesta di I/O in ingresso, imposta l'oggetto richiesta come padre degli oggetti di memoria associati. Pertanto, l'oggetto memoria non può essere mantenuto più della durata dell'oggetto request. Quando il driver basato su framework completa la richiesta di I/O, il framework elimina l'oggetto richiesta e l'oggetto memoria, quindi gli handle a questi due oggetti diventano non validi.

Tuttavia, il buffer sottostante è diverso. A seconda del componente creato il buffer e del modo in cui è stato creato il buffer, il buffer potrebbe avere un numero di riferimenti e potrebbe essere di proprietà dell'oggetto memoria oppure potrebbe non essere. Se l'oggetto memoria possiede il buffer, il buffer ha un numero di riferimenti e la relativa durata è limitata a quella dell'oggetto memoria. Se un altro componente ha creato il buffer, la durata del buffer e l'oggetto memoria non sono correlati.

Un driver basato su framework può anche creare oggetti di richiesta personalizzati da inviare alle destinazioni di I/O. Una richiesta creata dal driver può riutilizzare un oggetto memoria esistente ricevuto dal driver in una richiesta di I/O. Un driver che invia spesso richieste alle destinazioni di I/O può riutilizzare gli oggetti richiesta creati.

Informazioni sulla durata dell'oggetto request, sull'oggetto memoria e sul buffer sottostante è importante assicurarsi che il driver non tenti di fare riferimento a un handle o a un puntatore del buffer non valido.

Considerare gli scenari di utilizzo seguenti:

Scenario 1: driver riceve una richiesta di I/O da KMDF, la gestisce e la completa.

Nello scenario più semplice, KMDF invia una richiesta al driver, che esegue L/O e completa la richiesta. In questo caso, il buffer sottostante potrebbe essere stato creato da un'applicazione in modalità utente, da un altro driver o dal sistema operativo stesso. Per informazioni su come accedere ai buffer, vedere Accesso ai buffer dei dati in Framework-Based driver.

Al termine della richiesta, il framework elimina l'oggetto memory. Il puntatore del buffer non è quindi valido.

Scenario 2: driver riceve una richiesta di I/O da KMDF e la inoltra a una destinazione di I/O.

In questo scenario il driver inoltra la richiesta a una destinazione di I/O. Il codice di esempio seguente illustra come un driver recupera un handle all'oggetto memory da un oggetto richiesta in ingresso, formatta la richiesta di inviare alla destinazione I/O e invia la richiesta:

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 la destinazione di I/O ha completato la richiesta, il framework chiama il callback di completamento impostato per la richiesta. Il codice seguente mostra un callback di completamento semplice:

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 il driver chiama WdfRequestComplete dal callback di completamento, il framework elimina l'oggetto memory. L'handle dell'oggetto memoria recuperato dal driver non è ora valido.

Scenario 3: driver genera una richiesta di I/O che usa un oggetto memoria esistente.

Alcuni driver eseguono richieste di I/O personalizzate e li inviano alle destinazioni di I/O, rappresentate dagli oggetti di destinazione I/O. Il driver può creare un oggetto richiesta personalizzato o riutilizzare un oggetto richiesta creato da framework. Usando una tecnica, un driver può riutilizzare un oggetto memoria da una richiesta precedente. Il driver non deve modificare il buffer sottostante, ma può passare un offset del buffer quando formatta la nuova richiesta di I/O.

Per informazioni su come formattare una nuova richiesta di I/O che usa un oggetto memoria esistente, vedere Invio di richieste di I/O a destinazioni di I/O generali.

Quando il framework formatta la richiesta di inviare alla destinazione di I/O, viene estratto un riferimento sull'oggetto memoria riciclata per conto dell'oggetto di destinazione I/O. L'oggetto di destinazione I/O mantiene questo riferimento fino a quando non si verifica una delle azioni seguenti:

  • La richiesta è stata completata.
  • Il driver riformatta nuovamente l'oggetto request chiamando uno dei metodi WdfIoTargetFormatRequestXxx o WdfIoTargetSendXxxSynchronously . Per altre informazioni su questi metodi, vedere Metodi oggetto di I/O framework.
  • Il driver chiama WdfRequestReuse.

Al termine della nuova richiesta di I/O, il framework chiama il callback di completamento di I/O impostato per questa richiesta. A questo punto, l'oggetto di destinazione I/O contiene ancora un riferimento sull'oggetto memory. Pertanto, nel callback di completamento di I/O, il driver deve chiamare WdfRequestReuse nell'oggetto richiesta creata dal driver prima di completare la richiesta originale da cui ha recuperato l'oggetto memory. Se il driver non chiama WdfRequestReuse, si verifica un controllo di bug a causa del riferimento aggiuntivo.

Scenario 4: Driver genera una richiesta di I/O che usa un nuovo oggetto di memoria.

Il framework offre tre modi per creare nuovi oggetti di memoria, a seconda dell'origine del buffer sottostante. Per altre informazioni, vedere Uso dei buffer di memoria.

Se il buffer viene allocato dal framework o da un elenco lookaside creato dal driver, l'oggetto memoria possiede il buffer, quindi il puntatore del buffer rimane valido fino a quando esiste l'oggetto memoria. I driver che generano richieste di I/O asincrone devono sempre usare buffer di proprietà degli oggetti di memoria in modo che il framework possa garantire che i buffer vengano mantenuti fino al completamento della richiesta di I/O al driver di emissione.

Se il driver assegna un buffer allocato in precedenza a un nuovo oggetto di memoria chiamando WdfMemoryCreatePreallocated, l'oggetto memoria non possiede il buffer. In questo caso, la durata dell'oggetto memoria e la durata del buffer sottostante non sono correlate. Il driver deve gestire la durata del buffer e non deve tentare di usare un puntatore del buffer non valido.

Scenario 5: Driver riutilizza un oggetto richiesta creato.

Un driver può riutilizzare gli oggetti di richiesta creati, ma deve reinizializzare ogni oggetto chiamando WdfRequestReuse prima di ogni riutilizzo. Per altre informazioni, vedere Riutilizzo degli oggetti richiesta framework.

Per il codice di esempio che reinizializza un oggetto request, vedere gli esempi Di tostapane e NdisEdge forniti con la versione kmDF.