存取 WDF 驅動程式中的資料緩衝區 (KMDF 或 UMDF)
當 Windows 驅動程式架構 (WDF) 驅動程式收到讀取、寫入或裝置 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 方法的要求。
指定緩衝區存取方法
對於讀取和寫入要求,驅動程式堆疊中的所有驅動程式都必須使用相同的方法來存取裝置的緩衝區,但最高階驅動程式除外,不論較低驅動程式會使用哪一種方法。
從 1.13 版開始,KMDF 驅動程式會為每個裝置呼叫 WdfDeviceInitSetIoTypeEx ,以指定所有裝置讀取和寫入要求的存取方法。 例如,如果驅動程式指定其中一個裝置的緩衝 I/O 方法,則 I/O 管理員會在傳遞讀取和寫入要求給該裝置的驅動程式時,使用緩衝的 I/O 方法。
對於裝置 I/O 控制要求,IOCTL) (IOCTL 控制項程式碼包含指定緩衝區存取方法的位。 因此,KMDF 驅動程式不需要採取任何動作來選取 IOCTLs 的緩衝方法。 如需 IOCTLs 的詳細資訊,請參閱 定義 I/O 控制程式碼。 不同于讀取和寫入要求,所有裝置的 IOCTLs 都不需要指定相同的存取方法。
UMDF 驅動程式會指定 架構用於讀取和寫入要求的存取方法喜好設定,以及裝置 I/O 控制要求。 UMDF 驅動程式所提供的值只是喜好設定,而且不保證架構會使用。 如需詳細資訊,請參閱 管理 UMDF 驅動程式中的緩衝區存取方法。
UMDF 驅動程式會為每個裝置呼叫 WdfDeviceInitSetIoTypeEx ,以指定所有裝置讀取、寫入和 IOCTL 要求的存取方法。 例如,如果驅動程式指定其中一個裝置的緩衝 I/O 方法,則架構會在將讀取、寫入和 IOCTL 要求傳遞給該裝置的驅動程式時,使用緩衝的 I/O 方法。
請注意 KMDF 與 UMDF 之間 IOCTL 的緩衝區存取技術差異。 KMDF 驅動程式不會指定 IOCTL 的緩衝區存取方法,而 UMDF 驅動程式則指定 IOCTLs 的緩衝區存取方法。
如果 WDF 驅動程式使用 I/O 目標所使用的 I/O 方法不正確技術來描述 I/O 要求的緩衝區,架構會更正緩衝區描述。 例如,如果驅動程式使用 MDL 來描述它傳遞至 WdfIoTargetSendReadSynchronously的緩衝區,而且如果 I/O 目標使用緩衝 I/O (,這需要使用虛擬位址來指定緩衝區,而不是使用 MDL) ,架構會將緩衝區描述從 MDL 轉換為虛擬位址和長度。 不過,如果您的驅動程式以正確的格式指定緩衝區,則更有效率。
如需架構記憶體物件、外觀清單、MDL 和本機緩衝區的相關資訊,請參閱 使用記憶體緩衝區。
如需何時刪除記憶體緩衝區的相關資訊,請參閱 記憶體緩衝區生命週期。
存取緩衝 I/O 的資料緩衝區
如果您的驅動程式使用緩衝 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 的所有 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。 如果架構收到指定「兩者」緩衝區方法的 IOCTL,它可以根據 INF 指示詞的存在,選擇性地將 IOCTL 要求的緩衝區存取方法轉換為緩衝 I/O 或直接 I/O。 如需詳細資訊 ,請參閱在 UMDF 驅動程式中管理緩衝區存取方法 。