I/O緩衝策略

I/O緩衝策略  
很明顯的,驅動程序和客戶應用程序經常需要進行數據交換,但我 們知道驅動程序和客戶應用程序可能不在同一個地址空間,因此操 作系統必須解決兩者之間的數據交換。這就就設計到設備的I/O緩衝策略。  


讀寫請求的I/O緩衝策略  
前面說到通過設置Device對象的Flag可以選擇控制處理讀寫請求的 I/O緩衝策略。下面對這些緩衝策略分別做一介紹。  


1、 緩衝I/O(DO_BUFFERED_IO)  
在讀寫請求的一開始,I/O管理器檢查用戶緩衝區的可訪問性,然 後分配與調用者的緩衝區一樣大的非份頁池,並把它的地址放在 IRP的AssociatedIrp.SystemBuffer域中。驅動程序就利用這個域來進行實際數據的傳輸。  

對於IRP_MJ_READ讀請求,I/O管理器還把IRP的UserBuffer域設置 成調用者緩衝區的用戶空間地址。當請求完成時,I/O管理器利用 這個地址將數據從驅動程序的系統空間拷貝回調用者的緩衝區。對 於IRP_MJ_WRITE寫請求,UserBuffer被設置爲NULL,並把用戶緩衝 區的數據拷貝到系統緩衝區中。  


2、 直接I/O(DO_DIRECT_IO)  

I/O管理器首先檢查用戶緩衝區的可訪問性,並在物理內存中鎖定 它。然後它爲該緩衝區創建一個內存描述表(MDL),並把MDL的地址 存放在IRP的MdlAddress域中。AssociatedIrp.SystemBuffer和 UserBuffer都被設置爲NULL。驅動程序可以調用函數 MmGetSystemAddressForMdl得到用戶緩衝區的系統空間地址,從而 進行數據操作。這個函數將調用者的緩衝區映射到非份頁的地址空 間。驅動程序完成I/O請求後,系統自動從系統空間解除緩衝區的 映射。  


3、 這兩種方法都不是  

這種情況比較少用,因爲這需要驅動程序自己來處理緩衝問題。 I/O管理器僅把調用者緩衝區的用戶空間地址放到IRP的UserBuffer 域中。我們並不推薦這種方式。  

IOCTL緩衝區的緩衝策略  
IOCTL請求涉及來自調用者的輸入緩衝區和返回到調用者的輸出 緩衝區。爲了理解IOCTL請求,我們先來看看WIN32 API DeviceIoControl函數的原型。  
BOOL DeviceIoControl (  
HANDLE hDevice, // 設備句柄  
DWORD dwIoControlCode, // IOCTL請求操作代碼  
LPVOID lpInBuffer, // 輸入緩衝區地址  
DWORD nInBufferSize, // 輸入緩衝區大小  

LPVOID lpOutBuffer, // 輸出緩衝區地址  

DWORD nOutBufferSize, // 輸出緩衝區大小  

LPDWORD lpBytesReturned, // 存放返回字節數的指針  

LPOVERLAPPED lpOverlapped // 用於同步操作的Overlapped結構體指針  

);  


IOCTL請求有四種緩衝策略,下面一一介紹。  


1、 輸入輸出緩衝I/O(METHOD_BUFFERED)  

I/O管理器首先分配一個非份頁池,它足夠大地存放調用者的輸 入或輸出緩衝區(不管哪個更大)。非份頁緩衝區的地址放在IRP的AssociatedIrp.Sys temBuffer域中,然後把IOCTL的輸入數據拷貝 到這個非份頁緩衝區中,並把IRP的UserBuffer域設置成調用者輸 出緩衝區的用戶空間地址。當驅動程序完成IOCTL請求時,I/O管理 器將這個非份頁緩衝區中的數據拷貝到調用者的輸出緩衝區。 注意這裏同一個非份頁池同時用於輸入和輸出緩衝區,因此驅動 程序在向緩衝區寫東西之前應該把輸入的所有數據讀出來。  

2、 直接輸入緩衝輸出I/O(METHOD_IN_DIRECT)  

I/O管理器首先檢查調用者輸入緩衝區的可訪問性,並在物理內存 中將其鎖定。然後爲該輸入緩衝區創建一個MDL,並把指定該MDL的 指針存放到IRP的MdlAddress域中。 同時,I/O管理器還在非份頁池中分配一輸出緩衝區,並把這個緩衝 區的地址存放在IRP的AssociatedIrp.SystemBuffer域中,並把IRP 的UserBuffer域設置成調用者輸出緩衝區的用戶空間地址。當驅動 程序完成IOCTL請求時,I/O管理器將非份頁緩衝區中的數據拷貝到 調用者的輸出緩衝區。  


3、 緩衝輸入直接輸出I/O(METHOD_OUT_DIRECT)  

I/O管理器首先檢查調用者輸出緩衝區的可訪問性,並在物理內存中 將其鎖定。然後爲該輸出緩衝區創建一個MDL,並把指定該MDL的指針 存放到IRP的MdlAddress域中。同時,I/O管理器還在非份頁池中分配一輸入緩衝區,並把這個緩衝區的地址存放在IRP的AssociatedIrp.SystemBuffer域中, 同時把 調用者用戶輸入緩衝區中的數據拷貝到系統緩衝區中,並把IRP的 UserBuffer域設置爲NULL。  


4、 上面三種方法都不是(METHOD_NEITHER)  

I/O管理器把調用者的輸入緩衝區的地址放到IRP當前I/O堆棧單元的Parameters.Devi ceIoControl.Type3InputBuffer域中,把輸出緩衝 區的地址存放到IRP的UserBuffer域中。這兩個地址都是用戶空間地 址。  

從上面的說明可以看出,在執行緩衝I/O時,I/O管理器將在非份頁池 中分配內存,如果調用者的緩衝區比較大時,分配的非份頁池也將 比較大。非份頁池是系統比較寶貴的資源,因此,如果調用者的緩 衝區比較大時,我們一般採用直接I/O的方式(例如磁盤讀寫請求等), 這樣不僅節省系統資源,另一方面由於省去了I/O管理器在系統緩衝 區和調用者緩衝區之間的數據拷貝,也提高了效率,這對存在大量 數據傳送的驅動程序尤其明顯。  
可以注意到DDK中的Samples下,幾乎所有的例程的讀寫請求都是直 接I/O的,而對於IOCTL請求則是緩衝區I/O的居多。  
下面以changerDisk的IRP_MJ_DEVICE_CONTROL例程中的一段程序來 加強IOCTL緩衝策略的認識和用法。在該例中所有的IOCTL請求的緩 衝策略都是METHOD_BUFFERED。  


NTSTATUS  

ChangerDiskDeviceControl(  

PDEVICE_OBJECT DeviceObject,  

PIRP Irp  

)  

{  

PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);  

ULONG ControlCode;  

ULONG InputLength, OutputLength;  

PVOID InputBuffer, OutputBuffer;  

…  

ControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;  

// IOCTL請求功能代碼存放在IRP當前I/O堆棧單元的  

// Parameters.DeviceIoControl.IoControlCode中。;  


InputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;  

// 輸入緩衝區的大小存放在IRP當前I/O堆棧單元的  

// Parameters.DeviceIoControl.InputBufferLength中。  


OutputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;  

// 輸出緩衝區的大小存放在IRP當前I/O堆棧單元的  

// Parameters.DeviceIoControl. OutputBufferLength中。  


InputBuffer = OutputBuffer = Irp->AssociatedIrp.SystemBuffer;  

// 由於本例中所有的IOCTL請求都是輸入輸出緩衝的,所以輸入輸出緩衝區 // 的地址就是IRP的AssociatedIrp.SystemBuffer域。  

….  

}  
發佈了9 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章