天天看點

50、Windows驅動程式模型設計筆記(八),IRP

3、派遣例程的職責<?xml:namespace prefix = o />

•派遣函數立即完成該IRP。

•把該IRP傳遞到處于同一堆棧的下層驅動程式。

•排隊該IRP以便由這個驅動程式中的其它例程來處理。

    每個裝置對象都自帶一個請求隊列對象,下面是使用這個隊列的标準方法:

NTSTATUS DispatchXxx(...)

{

...

IoMarkIrpPending(Irp);

IoStartPacket(device, Irp, NULL, NULL);

return STATUS_PENDING;

}

一旦我們調用了IoStartPacket函數,就不要再碰IRP。因為在該函數傳回之前,IRP可能已經被完成并且其占用的記憶體可能被釋放,而我們擁有的該IRP的指針也許是無效的。

4、StartIo例程

每處理一個IRP,I/O管理器就調用一次StartIo例程。StartIo的工作是就着手處理IRP。

5、中斷服務例程

用IoConnectInterrupt函數“鈎住”一個中斷,該函數的一個參數就是ISR的位址。一個ISR最可能做的事就是排程DPC例程(推遲過程調用)。而DPC的目的就是讓你做某些事情,如調用IoCompleteRequest。

表示. IoCompleteRequest的優先級推進值

推進值常量

優先級推進值

IO_NO_INCREMENT

IO_CD_ROM_INCREMENT

1

IO_DISK_INCREMENT

IO_KEYBOARD_INCREMENT

6

IO_MAILSLOT_INCREMENT

2

IO_MOUSE_INCREMENT

IO_NAMED_PIPE_INCREMENT

IO_NETWORK_INCREMENT

IO_PARALLEL_INCREMENT

IO_SERIAL_INCREMENT

IO_SOUND_INCREMENT

8

IO_VIDEO_INCREMENT

    不要以專用狀态代碼STATUS_PENDING來完成一個IRP。派遣例程經常要使用STATUS_PENDING代碼作為傳回值,但你決不能在IoStatus.Status中設定這個值。

6、完成例程

    IoSetCompletionRoutine将把完成例程位址和上下文參數安裝到下一個IO_STACK_LOCATION中,即下一層驅動程式将在那個堆棧單元中找到這些參數。是以,最底層的驅動程式不應該安裝一個完成例程。

    完成例程通常在DISPATCH_LEVEL級和任意線程上下文中被調用,但有時也在PASSIVE_LEVEL或APC_LEVEL級被調用。為了适應大多數情況(DISPATCH_LEVEL),完成例程應存在于非分頁記憶體中,并且僅使用可在DISPATCH_LEVEL級上調用的服務例程。然而,為了适應在低級IRQL上調用該例程的可能情況,完成例程不應調用像KeAcquireSpinLockAtDpcLevel這樣的函數,因為這些函數假定開始執行于DISPATCH_LEVEL級上。

    在完成例程内部,一個IoGetCurrentIrpStackLocation調用将獲得上一層堆棧單元的指針。上層堆棧單元的完成例程不應該依賴任何下層堆棧單元中的内容。為了加強這個規則,IoCompleteRequest在調用完成例程前清除了下一個堆棧單元中的大部分内容。

50、Windows驅動程式模型設計筆記(八),IRP

if (Irp->PendingReturned)

    所有不傳回STATUS_MORE_PROCESSING_REQUIRED狀态的完成例程都需要這兩行代碼。

7、取消I/O請求

    為了在核心模式中取消一個請求,IRP的建立者需調用IoCancelIrp函數。如果某線程終止時,它發出的請求仍然未完成,則作業系統自動為每個IRP調用IoCancelIrp。使用者模式應用程式調用CancelIo函數可以取消給定線程發出的所有未完成的異步操作。IoCancelIrp僅僅是簡單地設定IRP的Cancel标志位然後調用IRP的取消例程。即:它并不知道你在是否修改過IRP指針,也不知道你是否正在處理這個IRP,是以它必須依靠一個你提供的取消例程來做大部分IRP取消工作。

關于取消IO更多的關于多CPU的讨論,見參考文獻[1]。

[1] Windows驅動程式模型設計