記憶體緩衝區生命週期
記憶體緩衝區的生命週期會跨越從建立緩衝區到刪除緩衝區的時間。 本主題描述緩衝區使用案例,以及緩衝區刪除時的影響。
在核心模式驅動程式架構 (KMDF) 中,要求物件代表 I/O 要求。 每個要求物件都與一或多個記憶體物件相關聯,而每個記憶體物件都代表用於要求中輸入或輸出的緩衝區。
當架構建立要求和記憶體物件來代表傳入 I/O 要求時,它會將要求物件設定為相關聯記憶體物件的父代。 因此,記憶體物件不能超過要求物件的存留期。 當架構型驅動程式完成 I/O 要求時,架構會刪除要求物件和記憶體物件,因此這兩個物件的控制碼會變成無效。
不過,基礎緩衝區不同。 根據哪個元件建立緩衝區及其建立緩衝區的方式而定,緩衝區可能會有參考計數,而且可能是記憶體物件所擁有的,或者可能不是。 如果記憶體物件擁有緩衝區,則緩衝區具有參考計數,而且其存留期僅限於記憶體物件的存留期。 如果有些其他元件建立了緩衝區,則緩衝區和記憶體物件的存留期並不相關。
架構型驅動程式也可以建立自己的要求物件,以傳送至 I/O 目標。 驅動程式建立的要求可以重複使用驅動程式在 I/O 要求中收到的現有記憶體物件。 經常將要求傳送至 I/O 目標的驅動程式可以重複使用它所建立 的要求物件 。
瞭解要求物件的存留期、記憶體物件和基礎緩衝區很重要,以確保驅動程式不會嘗試參考不正確控制碼或緩衝區指標。
請考慮下列使用案例:
- 案例 1: 驅動程式會從 KMDF 接收 I/O 要求、處理它,並完成它。
- 案例 2: 驅動程式會收到 KMDF 的 I/O 要求,並將其轉送至 I/O 目標。
- 案例 3: 驅動程式發出使用現有記憶體物件的 I/O 要求。
- 案例 4: 驅動程式發出使用新記憶體物件的 I/O 要求。
- 案例 5: 驅動程式會重複使用它建立的要求物件。
案例 1:驅動程式會從 KMDF 接收 I/O 要求、處理它,並完成它。
在最簡單的案例中,KMDF 會將要求分派給驅動程式,以執行 I/O 並完成要求。 在此情況下,基礎緩衝區可能是由使用者模式應用程式、另一個驅動程式或作業系統本身所建立。 如需如何存取緩衝區的資訊,請參閱 存取Framework-Based驅動程式中的資料緩衝區。
當驅動程式 完成要求時,架構會刪除記憶體物件。 緩衝區指標接著無效。
案例 2:驅動程式會收到 KMDF 的 I/O 要求,並將其轉送至 I/O 目標。
在此案例中,驅動程式 會將要求轉送 至 I/O 目標。 下列範例程式碼示範驅動程式如何從傳入要求物件擷取記憶體物件的控制碼、將要求格式化為傳送至 I/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;
}
當 I/O 目標完成要求時,架構會呼叫驅動程式為要求設定的完成回呼。 下列程式碼顯示簡單的完成回呼:
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;
}
當驅動程式從完成回呼呼叫 WdfRequestComplete 時,架構會刪除記憶體物件。 驅動程式擷取的記憶體物件控制碼現在無效。
案例 3:驅動程式發出使用現有記憶體物件的 I/O 要求。
有些驅動程式會發出自己的 I/O 要求,並將其傳送至 I/O 目標,由 I/O 目標物件表示。 驅動程式可以建立自己的要求物件,或 重複使用架構建立的要求物件。 使用任一技術,驅動程式可以重複使用先前要求的記憶體物件。 驅動程式不得變更基礎緩衝區,但可以在格式化新的 I/O 要求時傳遞緩衝區位移。
如需如何格式化使用現有記憶體物件之新 I/O 要求的資訊,請參閱 將 I/O 要求傳送至一般 I/O 目標。
當架構格式化傳送至 I/O 目標的要求時,它會代表 I/O 目標物件在回收的記憶體物件上取出參考。 I/O 目標物件會保留此參考,直到發生下列其中一個動作為止:
- 要求已完成。
- 驅動程式會呼叫其中一個 WdfIoTargetFormatRequestXxx 或 WdfIoTargetSendXxxSynchronously 方法來重新格式化要求物件。 如需這些方法的詳細資訊,請參閱 Framework I/O Target Object Methods。
- 驅動程式會呼叫 WdfRequestReuse。
當新的 I/O 要求完成時,架構會呼叫驅動程式為此要求設定的 I/O 完成回呼。 此時,I/O 目標物件仍會保留記憶體物件的參考。 因此,在 I/O 完成回呼中,驅動程式必須在驅動程式建立的要求物件上呼叫 WdfRequestReuse ,才能完成從中擷取記憶體物件的原始要求。 如果驅動程式未呼叫 WdfRequestReuse,就會因為額外的參考而發生錯誤檢查。
案例 4:驅動程式發出使用新記憶體物件的 I/O 要求。
架構提供三種方式讓驅動程式建立新的記憶體物件,視基礎緩衝區的來源而定。 如需詳細資訊,請參閱 使用記憶體緩衝區。
如果緩衝區是由架構配置,或從驅動程式建立 的 lookaside 清單中配置,則記憶體物件會擁有緩衝區,因此只要記憶體物件存在,緩衝區指標就會維持有效狀態。 發出非同步 I/O 要求的驅動程式應該一律使用記憶體物件所擁有的緩衝區,讓架構可以確保緩衝區持續存在,直到 I/O 要求完成回到發出驅動程式為止。
如果驅動程式藉由呼叫 WdfMemoryCreatePreallocated將先前配置的緩衝區指派給新的記憶體物件,則記憶體物件不會擁有緩衝區。 在此情況下,記憶體物件的存留期和基礎緩衝區的存留期並不相關。 驅動程式必須管理緩衝區的存留期,而且不得嘗試使用不正確緩衝區指標。
案例 5:驅動程式會重複使用它建立的要求物件。
驅動程式可以重複使用它建立的要求物件,但必須在每次重複使用之前呼叫 WdfRequestReuse 來重新初始化每個這類物件。 如需詳細資訊,請參閱 重複使用 Framework 要求物件。