从IRP说起
從IRP說起
?
IRP(I/O request package)是操作系統(tǒng)內(nèi)核的一個(gè)數(shù)據(jù)結(jié)構(gòu)。應(yīng)用程序與驅(qū)動(dòng)程序進(jìn)行通信需要通過IRP包。當(dāng)上層應(yīng)用程序需要與驅(qū)動(dòng)通信的時(shí)候,通過調(diào)用一定的API函數(shù),IO管理器針對(duì)不同的API產(chǎn)生不同的IRP,IRP被傳遞到驅(qū)動(dòng)內(nèi)部不同的分發(fā)函數(shù)進(jìn)行處理。對(duì)于不會(huì)處理的IRP包需要提供一個(gè)默認(rèn)的分發(fā)函數(shù)來處理。
現(xiàn)在我們來看一下IRP的結(jié)構(gòu):
typedef struct _IRP {
? ?…
? PMDL? MdlAddress;
? ULONG? Flags;
? union {
struct _IRP? *MasterIrp;
…
??? PVOID? SystemBuffer;
? } AssociatedIrp;
? LIST_ENTRY? ThreadListEntry;? //用來將 IRP掛入某個(gè)線程的 IrpList隊(duì)列??
? IO_STATUS_BLOCK? IoStatus;? //用來返回操作的完成狀況? ?
? KPROCESSOR_MODE? RequestorMode;??
? BOOLEAN? PendingReturned;??
? CHAR? StackCount;??
? CHAR? CurrentLocation;??
? …
? BOOLEAN? Cancel;
? KIRQL? CancelIrql;
? …
? PDRIVER_CANCEL? CancelRoutine;
? PVOID UserBuffer;
? union {
??? struct {
??? …
??? union {
????? KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
????? struct {
??????? PVOID? DriverContext[4];
????? };
??? };
??? …
??? PETHREAD? Thread;
??? …
LIST_ENTRY? ListEntry;
…
??? } Overlay;
??? …
? } Tail;
} IRP, *PIRP;???
MSDN 說IRP是一個(gè)半透明結(jié)構(gòu),開發(fā)者只能訪問其中透明的部分。
其實(shí)數(shù)據(jù)結(jié)構(gòu) IRP 只是"I/O 請(qǐng)求包"IRP的頭部,在 IRP 數(shù)據(jù)結(jié)構(gòu)的后面還有一個(gè)IO_STACK_LOCATION 數(shù)據(jù)結(jié)構(gòu)的數(shù)組,數(shù)組的大小則取決于 IRP 數(shù)據(jù)結(jié)構(gòu)中的StackCount,其數(shù)值來自堆疊中頂層設(shè)備對(duì)象的 StackSize 字段。這樣,就在 IRP 中為目標(biāo)設(shè)備對(duì)象堆疊中的每一層即每個(gè)模塊都準(zhǔn)備好了一個(gè) IO_STACK_LOCATION 數(shù)據(jù)結(jié)構(gòu)。而CurrentLocation,則是用于該數(shù)組的下標(biāo),說明目前是在堆疊中的哪一層,因而正在使用哪一個(gè) IO_STACK_LOCATION 數(shù)據(jù)結(jié)構(gòu)。
? 先來對(duì)IRP結(jié)構(gòu)進(jìn)行說明。
第一個(gè)參數(shù) PMDL? MdlAddress:
MdlAddress域指向一個(gè)內(nèi)存描述符表(MDL),描述了一個(gè)與該IO請(qǐng)求關(guān)聯(lián)的用戶模式緩沖區(qū)。如果頂級(jí)設(shè)備對(duì)象的Flags域?yàn)镈O_DIRECT_IO,則I/O管理器為 IRP_MJ_READ或 IRP_MJ_WRITE請(qǐng)求創(chuàng)建這個(gè)MDL。如果一個(gè)IRP_MJ_DEVICE_CONTROL請(qǐng)求的控制代碼指定METHOD_IN_DIRECT或METHOD_OUT_DIRECT操作方式,則I/O管理器為該請(qǐng)求使用的輸出緩沖區(qū)創(chuàng)建一個(gè)MDL。
?
下一個(gè)參數(shù):AssociatedIrp
我們WDM驅(qū)動(dòng)會(huì)用到AssociatedIrp.SystemBuffer,這是一個(gè)指向系統(tǒng)空間的緩沖區(qū)。當(dāng)使用直接IO的時(shí)候,這個(gè)緩沖區(qū)的用途由與IRP相關(guān)的Majorfunction決定。對(duì)于IRP_MJ_READ和IRP_MJ_WRITE,則不會(huì)用到這個(gè)緩沖區(qū)。對(duì)于IRP_MJ_DEVICE_CONTROL 或 IRP_MJ_INTERNAL_DEVICE_CONTROL這兩類IRP,該緩沖區(qū)被作為DeviceIoControl函數(shù)的輸入緩沖區(qū)。該緩沖區(qū)的長(zhǎng)度由IO_STACK_LOCATION結(jié)構(gòu)(后面會(huì)講到該結(jié)構(gòu))中的Parameters.DeviceIoControl.InputBufferLength 成員來確定。
?
IoStatus(IO_STATUS_BLOCK)是一個(gè)僅包含兩個(gè)域的結(jié)構(gòu),驅(qū)動(dòng)程序在最終完成請(qǐng)求時(shí)設(shè)置這個(gè)結(jié)構(gòu)。IoStatus.Status 表示IRP完成狀態(tài),IoStatus.information的值與請(qǐng)求相關(guān),如果是數(shù)據(jù)傳輸請(qǐng)求,則將該域設(shè)置為傳輸?shù)淖止?jié)數(shù)。
?
CurrentLocation(CHAR)和Tail.Overlay.CurrentStackLocation(PIO_STACK_LOCATION)沒有公開為驅(qū)動(dòng)程序使用,但可以通過IoGetCurrentIrpStackLocation函數(shù)獲取這些信息。
?
說到IRP結(jié)構(gòu)的CurrentLocation,我們可以來看一下IO_STACK_LOCATION結(jié)構(gòu)了。
任何內(nèi)核模式程序在創(chuàng)建一個(gè)IRP時(shí),同時(shí)還創(chuàng)建了一個(gè)與之關(guān)聯(lián)的 IO_STACK_LOCATION 結(jié)構(gòu)數(shù)組:數(shù)組中的每個(gè)堆棧單元都對(duì)應(yīng)一個(gè)將處理該IRP的驅(qū)動(dòng)程序,堆棧單元中包含該IRP的類型代碼和參數(shù)信息以及完成函數(shù)的地址。
說簡(jiǎn)單些就是在分層驅(qū)動(dòng)中使用CurrentLocation來記錄IRP到達(dá)了哪一層,在不同的層有對(duì)應(yīng)的處理函數(shù)(通過IO_STACK_LOCATION關(guān)聯(lián)),對(duì)IRP進(jìn)行特定的處理。
?
?
?
?
IO_STACK_LOCATION結(jié)構(gòu)為:
typedef struct _IO_STACK_LOCATION {
? UCHAR? MajorFunction;
? UCHAR? MinorFunction;
? UCHAR? Flags;
? UCHAR? Control;
? Union
???????? {
???????? …
???????? }Parameters;
PDEVICE_OBJECT??DeviceObject;??
PFILE_OBJECT??FileObject;
?? PIO_COMPLETION_ROUTINE CompletionRoutine;
?? PVOID? context;
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
MajorFunction指示驅(qū)動(dòng)程序應(yīng)該使用哪個(gè)函數(shù)來處理IO請(qǐng)求。
MinorFunction 進(jìn)一步指出該IRP屬于哪個(gè)主功能類
Flags 表明IO請(qǐng)求類型。
DeviceObject(PDEVICE_OBJECT)是與該堆棧單元對(duì)應(yīng)的設(shè)備對(duì)象的地址。該域由IoCallDriver函數(shù)負(fù)責(zé)填寫。
CompletionRoutine(PIO_COMPLETION_ROUTINE)是一個(gè)I/O完成例程的地址,該地址是由與這個(gè)堆棧單元對(duì)應(yīng)的驅(qū)動(dòng)程序的更上一層驅(qū)動(dòng)程序設(shè)置的。通過調(diào)用IoSetCompletionRoutine函數(shù)來設(shè)置。設(shè)備堆棧的最低一級(jí)驅(qū)動(dòng)程序并不需要完成例程,因?yàn)樗鼈儽仨氈苯油瓿烧?qǐng)求。然而,請(qǐng)求的發(fā)起者有時(shí)確實(shí)需要一個(gè)完成例程,但通常沒有自己的堆棧單元。這就是為什么每一級(jí)驅(qū)動(dòng)程序都使用下一級(jí)驅(qū)動(dòng)程序的堆棧單元保存自己完成例程指針的原因。
?
現(xiàn)在對(duì)IRP和IO_STACK_LOCATION都有了一個(gè)初步的認(rèn)識(shí)。當(dāng)驅(qū)動(dòng)程序?qū)RP完成了操作(對(duì)各個(gè)域的讀寫)之后,需要調(diào)用IoCompleteRequest表明IRP處理已經(jīng)結(jié)束,并將IRP交還給IO管理器。
VOID IoCompleteRequest(
? __in??PIRP Irp,
? __in??CCHAR PriorityBoost
);
第二個(gè)參數(shù)一般設(shè)置為IO_NO_INCREMENT。具體可參見MSDN。
對(duì)缺省IRP我們可以這樣編寫函數(shù)來處理:
NTSTATUS xxxDispatchRoutine(IN PDEVICE_OBJECT ?do,IN PIRP Irp)
{
?????? PAGED_CODE();
?????? KdPrint(("Enter xxxDispatchRoutine\n"));
?????? Irp->IoStatus.Status = STATUS_SUCCESS;
?????? Irp->IoStatus.Information = 0; // no bytes xfered
?????? IoCompleteRequest( Irp, IO_NO_INCREMENT );
?????? KdPrint(("Leave xxxDispatchRoutine\n"));
?????? return STATUS_SUCCESS;
}
?
?
WDM驅(qū)動(dòng)是分層的,經(jīng)常需要將IRP包在各層驅(qū)動(dòng)中傳遞,負(fù)責(zé)IRP傳遞的函數(shù)有下面幾個(gè):IoCallDriver()?? IoSkipCurrentIrpStackLocation()?? IoCopyCurrentIrpStackLocationToNext()。
函數(shù)分別的定義為(注意函數(shù)的參數(shù)):
NTSTATUS IoCallDriver(
? __in?????PDEVICE_OBJECT DeviceObject,
? __inout??PIRP Irp
);
通過該函數(shù),將IRP送到指定設(shè)備(第一個(gè)參數(shù))的驅(qū)動(dòng)程序進(jìn)行處理。
VOID IoSkipCurrentIrpStackLocation(
? [in, out]??PIRP Irp
);
#define IoSkipCurrentIrpStackLocation( Irp ) { \
??? (Irp)->CurrentLocation++; \
??? (Irp)->Tail.Overlay.CurrentStackLocation++; }
該函數(shù)其實(shí)就是一個(gè)宏定義,設(shè)置IRP中IO_STACK_LOCATION的指針,上面兩個(gè)函數(shù)一般在過濾驅(qū)動(dòng)中配合使用:
IoSkipCurrentIrpStackLocation(Irp);//location+1
IoCallDriver(deviceExtension->nextLower, Irp);//location-1
執(zhí)行完上面兩步之后,location正好跟調(diào)用者一樣,IO_STACK_LOCATION中的內(nèi)容也不變。Filter driver常用此種手段轉(zhuǎn)發(fā)IRP:收到一個(gè)IRP,獲取或者修改其數(shù)據(jù),繼續(xù)轉(zhuǎn)發(fā),因?yàn)閘ocation沒變所以上層驅(qū)動(dòng)設(shè)置的CompleteRoutine依然會(huì)被filter之下的那個(gè)驅(qū)動(dòng)調(diào)用到,Filter driver 就像透明的一樣。
?
?
?
VOID IoCopyCurrentIrpStackLocationToNext(
? __inout??PIRP Irp
);
#define IoCopyCurrentIrpStackLocationToNext( Irp ) { \?
??? PIO_STACK_LOCATION __irpSp; \?
??? PIO_STACK_LOCATION __nextIrpSp; \?
??? __irpSp = IoGetCurrentIrpStackLocation( (Irp) ); \?
??? __nextIrpSp = IoGetNextIrpStackLocation( (Irp) ); \?
??? RtlCopyMemory( __nextIrpSp, __irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); \?
??? __nextIrpSp->Control = 0; }
可以看出該函數(shù)是一個(gè)宏定義,注意這里拷貝的是IRP stack,并不會(huì)影響下層的IRP stack。該函數(shù)一般和IoSetCompletionRoutine連用,一般用來處理異步的IRP包。每次調(diào)用IoCopyCurrentStackLocationToNext()函數(shù),就將本層的IRP stack 放當(dāng)下層的IRP stack頂端,當(dāng)IoCompleteRequest函數(shù)被調(diào)用也就是IRP包被處理完成之后,IRP stack 會(huì)一層層堆棧向上彈出,如果遇到IO_STACK_LOCATION的CompletionRoutine非空,則調(diào)用這個(gè)函數(shù),另外傳進(jìn)這個(gè)完成例程的是IO_STACK_LOCATION的子域Context。
?
VOID IoSetCompletionRoutine(
? __in??????PIRP Irp,
? __in_opt??PIO_COMPLETION_ROUTINE CompletionRoutine,
? __in_opt??PVOID Context,
? __in??????BOOLEAN InvokeOnSuccess,
? __in??????BOOLEAN InvokeOnError,
? __in??????BOOLEAN InvokeOnCancel
);
該函數(shù)設(shè)定一個(gè)CompletionRountine,當(dāng)IRP處理完成逐層彈出到設(shè)定了CompletionRountine的堆棧的時(shí)候,則通過這個(gè)CompletionRountine再次進(jìn)行處理。
最后再介紹一下獲取IRP當(dāng)前堆棧位置的函數(shù):
IoGetCurrentIrpStackLocation(PIRP Irp);
這其實(shí)是一個(gè)宏定義:
#define IoGetCurrentIrpStackLocation \
( Irp ) ( (Irp)->Tail.Overlay.CurrentStackLocation )
?
還有一個(gè)可獲得IRP下層堆棧:
IoGetNextIrpStackLocation(PIRP? Irp);
#define IoGetNextIrpStackLocation( Irp ) (\
(Irp)->Tail.Overlay.CurrentStackLocation - 1 )
轉(zhuǎn)載于:https://www.cnblogs.com/zhuyp1015/archive/2012/03/14/2396595.html
總結(jié)
- 上一篇: Objective C学习总结(二)
- 下一篇: Codeblocks 开发板调试简单教