搭配 PIO 使用直接 I/O
使用已程式化 I/O (PIO) 而非 DMA 的驅動程式,必須將使用者空間緩衝區加倍地對應到系統空間位址範圍。 下圖說明 I/O 管理員如何為使用直接 I/O 的 PIO 傳輸作業設定 IRP_MJ_READ 要求。
此圖顯示使用 PIO 的裝置如何處理相同的工作。
某些使用者空間虛擬位址的範圍代表當前執行緒的緩衝區,而該緩衝區的內容實際上可能儲存在一些物理上不連續的頁面中。 如果緩衝區長度非零,I/O 管理員會建立 MDL 來描述此緩衝區。
I/O 管理員會服務目前線程的讀取要求,其中線程會傳遞代表緩衝區的使用者空間虛擬位址範圍。
I/O 管理員或 FSD 會檢查使用者提供的緩衝區是否可存取。 如果 I/O 管理員已建立 MDL,它會使用 MDL 呼叫 MmProbeAndLockPages,以指定使用者緩衝區的虛擬位址範圍。 MmProbeAndLockPages 也會填入 MDL 中對應的實體地址範圍。
I/O 管理員會在要求傳輸作業的 IRP 中,提供 MDL (MdlAddress) 的指標。 在驅動程式完成 IRP 之後,I/O 管理員或文件系統呼叫 MmUnlockPages 之前,MDL 中所述的實體頁面會保持鎖定狀態並指派給緩衝區。 不過,即使在 IRP 傳送到設備驅動器或可能分層於裝置驅動程式的任何中繼驅動程式之前,這類 MDL 中的虛擬位址仍可能會變成看不見(且無效)。
如果驅動程式需要系統(虛擬)位址,驅動程式會使用 IRP 的 MdlAddress 指標呼叫 MmGetSystemAddressForMdlSafe,以將 MDL 中的使用者空間虛擬位址加倍對應至系統空間地址範圍。 在上圖中,AliasBuff 代表用來描述雙重映射位址的 MDL。
驅動程式會使用來自雙重映射的 MDL (AliasBuff) 的系統空間虛擬位址範圍,將資料讀入記憶體中。
當驅動程式透過呼叫 IoCompleteRequest完成 IRP 時,如果驅動程式曾呼叫 MmGetSystemAddressForMdlSafe,I/O 管理員或檔案系統將釋放 MDL 的雙重映射系統空間範圍。 I/O 管理員或檔案系統會解除鎖定 MDL 中所述的頁面,並代表驅動程式處置 MDL 和 IRP。 為了提升效能,驅動程式應避免將 MDL 實體地址與系統空間的雙重對應,如步驟 3 中所述,除非它們必須使用虛擬位址。 這樣做會不必要地占用系統的分頁表項,並可能降低驅動程式的效能和擴展性。 此外,如果系統用完頁面數據表項目,系統可能會當機,因為大部分較舊的驅動程式無法處理這種情況。
目前使用者執行緒的緩衝區和執行緒本身只有在它作為當前執行緒時,才能保證會駐留在物理記憶體中。 針對上圖所示的線程,其用戶緩衝區的內容可以在執行另一個進程的線程時分頁到次要記憶體。 當執行另一個程序的線程時,除非記憶體管理員已鎖定並保留包含原始線程緩衝區的對應實體頁面,否則可能會覆寫要求線程的緩衝區的系統物理記憶體。
不過,即使記憶體管理員保留了緩衝區的實體頁面,只要另一個線程當前運行,原有線程的虛擬位址就不會保持可見。 因此,驅動程式無法使用 MmGetMdlVirtualAddress 傳回的虛擬位址來存取記憶體。 此例程的呼叫端必須將結果傳遞至 MapTransfer(以及 IRP 的 MdlAddress 指標),才能使用封包型系統或總線主機 DMA 傳輸數據。