Windows內核-7-(IRP)I/O請求包

Windows內核-7-(IRP)I/O請求包

IRP(I/O Request Packet)就是一個進行I/O操作的請求包。

IRP是一個結構體,誰分配誰就得釋放,通常由執行體里的管理器,獲取內核驅動程式來分配。

IRP不是單獨的,只要創建了IRP就會跟著創建IRP的I/O棧,有一個棧是給內核驅動用的:

 

 

驅動需要調用IoGetCurrentIrpStackLocation函數來獲取內驅驅動對應的I/O棧。

I/O傳遞

I/O系統是以設備對象為中心,而不是以驅動對象為中心的。IRP可以在設備對象中傳來傳去:

 

 

有點類似於電腦網路的體系結構。但是不可否認的是I/O可以在設備直接進行各種傳遞,所以通常用來作為讀取文本數據。

具體的傳遞過程有點類似於棧。

IRP和I/O棧

 

 

這張圖是對前面第一張圖的細化。

分發常式(dispatch routine)

IRP最主要的一個作用就是拿來做分發常式,其實也就是分發的實例函數,就是對文件數據的傳遞。在我的理解里,就是對資訊的分發,讓大家一起用到數據。

所有的分發常式都有一個原型:

typedef NTSTATUS DRIVER_DISPATCH (
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp);

分發常式必須保存在驅動對象的MajorFunction裡面,且需要對應和驅動的連接狀態:

狀態描述
IRP_MJ_CREATE 獲取文件句柄,對應User下的CreateFile
IRP_MJ_CLOSE 關閉文件句柄,對應User下的CloseHandle
IRP_MJ_READ 對應ReadFile
IRP_MJ_WRITE 對應WriteFile
IRP_MJ_DEVICE_CONTROL 對應DeviceIoControl,即可讀又可寫
IRP_MJ_INTERNAL_DEVICE_CONTROL 和IRP_MJ_DEVICE_CONTROL差不多,但是只有Kernel可用

完成請求

一旦決定要處理IRP(意味著不會把該IRP傳遞給下一個設備對象),就必須要完成它。

比如:

NTSTATUS MyDispatchRoutine(PDEVICE_OBJECT, PIRP Irp) {
//...
Irp->IoStatus.Status = STATUS_XXX;
Irp->IoStatus.Information = NumberOfBytesTransfered; // depends on request type
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_XXX;
}
//這裡的IoCompleteRequest就是必須的

安全IRP操作

由於執行緒原因,很有可能一個去讀一個去寫,導致兩個只有一個拿到了想要的數據。

還有頁面記憶體的原因,修改的時候有可能一起修改了。

內核提供了兩種比較安全的處理方式:

1 緩衝I/O

2 直接I/O

緩衝I/O

要使用緩衝I/O只需要在設備對象的Flag欄位裡面添加一個DO_BUFFERED_IO

DeviceObject->Flags |= DO_BUFFERED_IO

緩衝I/O的原理就是在系統記憶體中開闢一段緩衝區,作為一個中間人。

比如說要寫,就先把內容寫到系統記憶體緩衝區里,然後系統記憶體緩衝區再寫入/讀取到緩衝區裡面。

 

 

 

 

 

 

 

 

 

 

 

直接I/O

直接緩衝採用物理記憶體映射來處理,將User和Kernel的緩衝區都指向物理記憶體裡面,並且鎖定在記憶體里,然後讀寫都採用它。這樣的好處是減少了調用的開銷。但是佔了部分記憶體。

 

 

 

 

 

 

 

 

 

 

 

 

 

IRP_MJ_DEVICE_CONTROL 調度

前面的緩衝和直接I/O是讀或寫操作,這裡的IRP_MJ_DEVICE_CONTROL操作是基於控制程式碼來選擇讀或寫兩個都可以的。

User態的IRP_MJ_DEVICE_CONTROL 和Kernel態的調用函數原型差不多:

BOOL DeviceIoControl(
HANDLE       hDevice,
DWORD       dwIoControlCode,
LPVOID       lpInBuffer,
DWORD       nInBufferSize,
LPVOID       lpOutBuffer,
DWORD       nOutBufferSize,
LPDWORD     lpBytesReturned,
LPOVERLAPPED lpOverlapped
);

dwIoControlCode:控制程式碼,由CTL_CODE宏的四個參數來處理。

Zero驅動程式

Client+Kernel的一個練手項目:

對於讀請求,會將提供的緩衝區清零然後輸出

對於寫請求,會把緩衝區消耗掉

源程式碼zodiacon/windowskernelprogrammingbook: The Windows Kernel Programming book samples (github.com)