驅動和應用層的三種通信方式

驅動程序和客戶應用程序經常需要進行數據交換,但我們知道驅動程序和客戶應用程序可能不在同一個地址空間,因此操作系統必須解決兩者之間的數據交換。
驅動層和應用層通信,主要是靠DeviceIoControl函數,下面是該函數的原型:
BOOL DeviceIoControl ( 
HANDLE hDevice, // 設備句柄 
DWORD dwIoControlCode, // IOCTL請求操作代碼 
LPVOID lpInBuffer, // 輸入緩衝區地址 
DWORD nInBufferSize, // 輸入緩衝區大小 
LPVOID lpOutBuffer, // 輸出緩衝區地址 
DWORD nOutBufferSize, // 輸出緩衝區大小 
LPDWORD lpBytesReturned, // 存放返回字節數的指針 
LPOVERLAPPED lpOverlapped // 用於同步操作的Overlapped結構體指針 
);

dwIoControlCode
要進行操作的控制碼。驅動程序可以通過CTL_CODE宏來組合定義一個控制碼,並在IRP_MJ_DEVICE_CONTROL的實現中進行控制碼的操作。在驅動層,irpStack->Parameters.DeviceIoControl.IoControlCode表示了這個控制碼。

IOCTL請求有四種緩衝策略,下面一一介紹。 
1、 輸入輸出緩衝I/O(METHOD_BUFFERED)
2、 直接輸入緩衝輸出I/O(METHOD_IN_DIRECT)
3、 緩衝輸入直接輸出I/O(METHOD_OUT_DIRECT)
4、 上面三種方法都不是(METHOD_NEITHER)

爲了對這些類型更詳細的描述,請看msdn上的解釋,我抄錄如下:

"緩衝"方法(METHOD_BUFFERED)
備註:在下面的討論中,"輸入"表示數據從用戶模式的應用程序到驅動程序,"輸出"表示數據從驅動程序到應用程序。

對於讀取請求,I/O 管理器分配一個與用戶模式的緩衝區大小相同的系統緩衝區。IRP 中的 SystemBuffer 字段包含系統地址。UserBuffer 字段包含初始的用戶緩衝區地址。當完成請求時,I/O 管理器將驅動程序已經提供的數據從系統緩衝區複製到用戶緩衝區。對於寫入請求,會分配一個系統緩衝區並將 SystemBuffer 設置爲地址。用戶緩衝區的內容會被複制到系統緩衝區,但是不設置 UserBuffer。對於 IOCTL 請求,會分配一個容量大小足以包含輸入緩衝區或輸出緩衝區的系統緩衝區,並將 SystemBuffer 設置爲分配的緩衝區地址。輸入緩衝區中的數據複製到系統緩衝區。UserBuffer 字段設置爲用戶模式輸出緩衝區地址。內核模式驅動程序應當只使用系統緩衝區,且不應使用 UserBuffer 中存儲的地址。

對於 IOCTL,驅動程序應當從系統緩衝區獲取輸入並將輸出寫入到系統緩衝區。當完成請求時,I/O 系統將輸出數據從系統緩衝區複製到用戶緩衝區。

"直接"方法(METHOD_IN/OUT_DIRECT)
對於讀取和寫入請求,用戶模式緩衝區會被鎖定,並且會創建一個內存描述符列表 (MDL)。MDL 地址會存儲在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均沒有任何含義。但是,驅動程序不應當更改這些字段的值。

對於 IOCTL 請求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同時有一個輸出緩衝區,則分配一個系統緩衝區(SystemBuffer 又有了地址)並將輸入數據複製到其中。如果有一個輸出緩衝區,且它被鎖定,則會創建 MDL 並設置 MdlAddress。UserBuffer 字段沒有任何含義。

"兩者都不"方法(METHOD_NEITHER)
對於讀取和寫入請求,UserBuffer 字段被設置爲指向初始的用戶緩衝區。不執行任何其他操作。SystemAddress 和 MdlAddress 沒有任何含義。對於 IOCTL 請求,I/O 管理器將 UserBuffer 設置爲初始的用戶輸出緩衝區,而且,它將當前 I/O 棧位置的 Parameters.DeviceIoControl.Type3InputBuffer 設置爲用戶輸入緩衝區。利用該 I/O 方法,由驅動程序來確定如何處理緩衝區:分配系統緩衝區或創建 MDL。

通常,驅動程序在訪問用戶數據時不應當將 UserBuffer 字段用作地址,即使當用戶緩衝區被鎖定時也是如此。這是由於在調用驅動程序時,在系統中可能看不到調用用戶的地址空間。(對於該規則的一個例外是,在最高層驅動程序將 IRP 向下傳遞到較低層的驅動程序之前,它可能需要使用 UserBuffer 來複制數據。)如果使用"直接"或"兩者都不"方法,在創建 MDL 之後,驅動程序可以使用 MmGetSystemAddressForMdl 函數來獲取有效的系統地址以訪問用戶緩衝區。


在驅動層,依傳輸類型的不同,輸入緩衝區的位置亦不同,見下表。
傳輸類型                                                   位置
METHOD_IN_DIRECT                irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT            irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED              irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                   irpStack->Parameters.DeviceIoControl.Type3InputBuffer

在驅動層,依傳輸類型的不同,輸出緩衝區的位置亦不同,見下表。
傳輸類型                                             位置
METHOD_IN_DIRECT                irp->MdlAddress
METHOD_OUT_DIRECT             irp->MdlAddress
METHOD_BUFFERED               irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                   irp->UserBuffer

所以只要確定了傳輸方式後,就可以根據各自的位置來讀取和寫入數據,從而實現應用層和驅動的通信。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章