當 Windows Driver Frameworks (WDF) 驅動程式收到讀取、寫入或裝置 I/O 控制項要求時,要求物件會包含輸入緩衝區、輸出緩衝區或兩者。
輸入緩衝區包含驅動程式所需的資訊。 針對寫入要求,此資訊通常是函式驅動程式必須傳送至裝置的數據。 針對裝置 I/O 控制要求,輸入緩衝區可能包含資訊,指出驅動程式必須執行的作業類型。
輸出緩衝區會從驅動程式接收資訊。 針對讀取要求,此資訊通常是函式驅動程式從裝置接收的數據。 針對裝置 I/O 控制要求,輸出緩衝區可能會接收要求 I/O 控制程式代碼所指定的狀態或其他資訊。
驅動程式用來存取要求數據緩衝區的技術取決於驅動程式用來存取裝置數據緩衝區的方法。 有三種存取方法:
- 緩衝 I/O。 I/O 管理員會建立與驅動程序共用的中繼緩衝區。
- 直接 I/O。 I/O 管理員會將緩衝區空間鎖定為物理記憶體,然後提供驅動程式直接存取緩衝區空間。
- 非緩衝 I/O 亦非直接 I/O。 I/O 管理員會為驅動程式提供要求的緩衝區空間虛擬位址。 I/O 管理員不會驗證要求的緩衝區空間,因此驅動程式必須確認緩衝區空間可供存取,並將緩衝區空間鎖定為物理記憶體。
Kernel-Mode Driver Framework (KMDF) 驅動程式可以使用三種存取方法中的任何一種。 User-Mode Driver Framework(UMDF) 驅動程式可以使用緩衝或直接 I/O 來處理讀取、寫入和 IOCTL 請求,並且可以 轉換那些指定 METHOD_NEITHER 方法的請求。
指定緩衝區存取方法
針對讀取和寫入要求,驅動程式堆疊中的所有驅動程式都必須使用相同的方法來存取裝置的緩衝區,但最高層級的驅動程式可以例外,使用“neither”方法,而不論較低層級的驅動程式使用的是哪種方法。
從 1.13 版開始,KMDF 驅動程式會針對每個裝置呼叫 WdfDeviceInitSetIoTypeEx ,為所有裝置的讀取和寫入要求指定存取方法。 例如,如果驅動程式為其其中一個裝置指定緩衝的 I/O 方法,I/O 管理員會在傳遞讀取和寫入要求給該裝置的驅動程式時,使用緩衝的 I/O 方法。
針對裝置 I/O 控制要求,I/O 控制碼(IOCTL)包含指定緩衝區存取方法的位元。 因此,KMDF 驅動程式不需要採取任何動作來選取IOCTL的緩衝方法。 如需IOCTLs的詳細資訊,請參閱 定義I/O控制碼。 不同於讀取和寫入要求,所有裝置的 IOCTL 都不需要指定相同的存取方法。
UMDF 驅動程式會指定架構用於讀寫要求及裝置 I/O 控制要求的存取方法偏好設定。 UMDF 驅動程式所提供的值只是喜好設定,而且不保證由架構使用。 如需詳細資訊,請參閱 在 UMDF 驅動程式中管理緩衝區存取方法。
UMDF 驅動程式會針對每個裝置呼叫 WdfDeviceInitSetIoTypeEx ,來指定裝置所有讀取、寫入和 IOCTL 要求的存取方法。 例如,如果驅動程式為其其中一個裝置指定緩衝的 I/O 方法,架構會在將讀取、寫入和 IOCTL 要求傳遞給該裝置的驅動程式時,使用緩衝的 I/O 方法。
請注意 KMDF 與 UMDF 之間的 IOCTL 緩衝區存取技術差異。 KMDF 驅動程式不會指定 IOCTL 的緩衝區存取方法,而 UMDF 驅動程式則指定 IOCTL 的緩衝區存取方法。
如果 WDF 驅動程式使用與 I/O 目標所需求的 I/O 方法不吻合的技術來描述 I/O 要求的緩衝區,框架會自動修正該緩衝區的描述。 例如,如果驅動程式使用 MDL 來描述它傳遞至 WdfIoTargetSendReadSynchronously 的緩衝區,而且如果 I/O 目標使用緩衝 I/O(這需要使用虛擬位址而非 MDL 指定緩衝區),架構會將緩衝區描述從 MDL 轉換成虛擬位址和長度。 不過,如果您的驅動程式以正確的格式指定緩衝區,則更有效率。
如需框架記憶體物件、旁路清單、MDL(記憶體描述子列表)和本機緩衝區的相關資訊,請參閱 使用記憶體緩衝區。
如需何時刪除記憶體緩衝區的相關信息,請參閱 記憶體緩衝區生命週期。
存取緩衝輸入/輸出的數據緩衝區
如果您的驅動程式使用緩衝的 I/O,其行為會根據數據要求的類型,以及其是否使用 KMDF 或 UMDF 而變更。
當 KMDF 驅動程式使用緩衝 I/O 時,I/O 管理員會建立一個中繼緩衝區,讓驅動程式可以存取每種要求類型。 發生的狀況如下:
- 寫入要求。 I/O 管理員會在呼叫驅動程式堆疊之前,先從呼叫應用程式的輸入緩衝區傳輸輸入資訊。 然後,KMDF 驅動程式會從中繼緩衝區讀取輸入資訊,並將其寫入裝置。
- 讀取要求。 KMDF 驅動程式會從裝置讀取資訊,並將其儲存在中繼緩衝區中。 然後,I/O 管理員會將輸出資料從中繼緩衝區複製到應用程式的輸出緩衝區。
- 裝置 I/O 控制要求。 KMDF 驅動程式會讀取或寫入中繼緩衝區中該要求的數據。
當 UMDF 驅動程式使用緩衝 I/O 時,驅動程式主機進程會根據要求類型建立一或兩個中繼緩衝區。 發生的狀況如下:
- 寫入要求。 架構會建立一個緩衝區、從呼叫應用程式的輸入緩衝區傳輸輸入資訊,然後呼叫驅動程式堆疊。 UMDF 驅動程式會從中繼緩衝區讀取輸入資訊,並將它寫入裝置。
- 讀取要求。 UMDF 驅動程式會從裝置讀取資訊,並將它儲存在架構建立的緩衝區中。 驅動程式主機進程會將輸出數據從中繼緩衝區複製到應用程式的輸出緩衝區。
- 裝置 I/O 控制要求。 架構會建立兩個緩衝區,分別對應至 IOCTL 的輸入和輸出緩衝區,讓驅動可存取。 架構會將輸入資訊從IOCTL複製到新的中繼緩衝區,並將其提供給驅動程式使用。 架構不會複製輸出緩衝區的內容,因此驅動程式不應該嘗試從中讀取(否則最終會讀取垃圾數據)。 驅動程式寫入輸出緩衝區的任何數據會複製回原始 IOCTL 緩衝區,並在 I/O 要求成功完成時傳回至應用程式。 請注意,驅動程式寫入輸入緩衝區的任何數據會被捨棄,而不會傳回給呼叫的應用程式。
若要擷取代表緩衝區的架構記憶體物件句柄,KMDF 和 UMDF 驅動程式都會呼叫 WdfRequestRetrieveInputMemory 或 WdfRequestRetrieveOutputMemory,視這是讀取或寫入要求而定。 然後,驅動程式可以藉由呼叫 WdfMemoryGetBuffer 來擷取緩衝區的指標。 若要讀取和寫入緩衝區,驅動程式會呼叫 WdfMemoryCopyFromBuffer 或 WdfMemoryCopyToBuffer。
若要擷取緩衝區的虛擬位址和長度,驅動程式會呼叫 WdfRequestRetrieveInputBuffer 或 WdfRequestRetrieveOutputBuffer。
若要配置和建置緩衝區的記憶體描述元清單 (MDL),KMDF 驅動程式會呼叫 WdfRequestRetrieveInputWdmMdl 或 WdfRequestRetrieveOutputWdmMdl。
存取直接 I/O 的數據緩衝區
如果您的驅動程式使用直接 I/O,則 I/O 管理員會驗證 I/O 請求發起者(通常是使用者模式應用程式)指定的緩衝區空間的可存取性,將緩衝區空間鎖定在實體記憶體中,然後為驅動程式提供這個緩衝區空間的直接存取權。
如果您的驅動程式已指定直接 I/O 的喜好設定,且已符合直接 I/O 的所有 UMDF 需求(請參閱 在 UMDF 驅動程式中管理緩衝區存取方法),架構會將它直接從 I/O 管理員收到的記憶體緩衝區對應到驅動程式的主機進程位址空間,進而為驅動程式提供對緩衝區空間的直接存取。
若要擷取代表緩衝區空間的架構記憶體物件的句柄,驅動程式會呼叫 WdfRequestRetrieveInputMemory 或 WdfRequestRetrieveOutputMemory。 然後,驅動程式可以藉由呼叫 WdfMemoryGetBuffer 來擷取緩衝區的指標。 若要讀取和寫入緩衝區,驅動程式會呼叫 WdfMemoryCopyFromBuffer 或 WdfMemoryCopyToBuffer。
若要擷取緩衝區空間的虛擬位址和長度,驅動程式會呼叫 WdfRequestRetrieveInputBuffer 或 WdfRequestRetrieveOutputBuffer。
如果裝置的驅動程式使用直接 I/O,I/O 管理員會使用 MDL 來描述緩衝區。 若要擷取緩衝區 MDL 的指標,KMDF 驅動程式會呼叫 WdfRequestRetrieveInputWdmMdl 或 WdfRequestRetrieveOutputWdmMdl。 UMDF 驅動程式無法存取 MDL。
存取非緩衝或非直接 I/O 的資料緩衝區
如果您的驅動程式使用一種稱為「非緩衝 I/O 亦非直接 I/O 方法」(簡稱為「兩者皆非」方法)的方法,I/O 管理器將直接提供驅動程式以 I/O 請求發起者所指定的緩衝區空間的虛擬位址。 I/O 管理員不會驗證 I/O 要求的緩衝區空間,因此驅動程式必須確認緩衝區空間可供存取,並將緩衝區空間鎖定為物理記憶體。
I/O 管理員提供的虛擬位址只能在提出 I/O 要求的程序上下文中存取。 只有驅動程式堆疊中最高層級的驅動程序保證會在原始程式的進程內容中執行。
若要取得 I/O 要求緩衝區空間的存取權,最高層級驅動程式必須提供 EvtIoInCallerContext 回呼函式。 架構會在每次收到驅動程式的 I/O 要求時呼叫此回呼函式。
如果要求的緩衝區存取方法「兩者都沒有」,KMDF 驅動程式必須針對每個緩衝區執行下列動作:
呼叫 WdfRequestRetrieveUnsafeUserInputBuffer 或 WdfRequestRetrieveUnsafeUserOutputBuffer 以取得緩衝區的虛擬位址。
呼叫 WdfRequestProbeAndLockUserBufferForRead 或 WdfRequestProbeAndLockUserBufferForWrite 來探查和鎖定緩衝區,並取得緩衝區架構記憶體物件的句柄。
將記憶體物件句柄儲存在要求 的內容空間中。
呼叫 WdfDeviceEnqueueRequest,它會將要求傳回至架構。
架構隨後會將要求新增至其中一個驅動程式的 I/O 佇列。 如果驅動程式已提供 要求處理程式,架構最終會呼叫適當的要求處理程式。
要求處理程式可以從要求的內容空間擷取要求的記憶體物件句柄。 驅動程式可以將句柄傳遞至 WdfMemoryGetBuffer ,以取得緩衝區的位址。
有時候,最高層級驅動程序必須使用上述步驟來存取使用者模式緩衝區,即使驅動程式未使用「兩者」存取方法也一樣。 例如,假設驅動程式使用緩衝的 I/O。 使用緩衝存取方法的 I/O 控制碼可能會傳送一個包含指向使用者模式緩衝區的嵌入指標的結構。 在這種情況下,驅動程式必須提供 EvtIoInCallerContext 回呼函式,以從 結構擷取指標,然後使用上述步驟 2 到 4。
UMDF 不支援緩衝處理或直接 I/O 類型緩衝區,因此 UMDF 驅動程式永遠不需要直接處理這種類型的緩衝區。
不過,如果架構收到這類緩衝區供 I/O 管理員讀取或寫入,它會根據驅動程式選取的存取方法,提供給 UMDF 驅動程式作為緩衝 I/O 或直接 I/O。 當框架收到指定 "neither" 緩衝方法的 IOCTL 時,可以根據 INF 指令的存在,選擇將 IOCTL 請求的緩衝存取方法轉換為緩衝 I/O 或直接 I/O。 如需詳細資訊 ,請參閱管理 UMDF 驅動程式中的緩衝區存取方法 。