50、Windows驱动程序模型设计笔记(八),IRP

3、派遣例程的职责

•派遣函数立即完成该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

0

IO_CD_ROM_INCREMENT

1

IO_DISK_INCREMENT

1

IO_KEYBOARD_INCREMENT

6

IO_MAILSLOT_INCREMENT

2

IO_MOUSE_INCREMENT

6

IO_NAMED_PIPE_INCREMENT

2

IO_NETWORK_INCREMENT

2

IO_PARALLEL_INCREMENT

1

IO_SERIAL_INCREMENT

2

IO_SOUND_INCREMENT

8

IO_VIDEO_INCREMENT

1

    不要以专用状态代码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在调用完成例程前清除了下一个堆栈单元中的大部分内容。

wps_clip_image-27419

if (Irp->PendingReturned)

IoMarkIrpPending(Irp);

    所有不返回STATUS_MORE_PROCESSING_REQUIRED状态的完成例程都需要这两行代码。

7、取消I/O请求

    为了在内核模式中取消一个请求,IRP的创建者需调用IoCancelIrp函数。如果某线程终止时,它发出的请求仍然未完成,则操作系统自动为每个IRP调用IoCancelIrp。用户模式应用程序调用CancelIo函数可以取消给定线程发出的所有未完成的异步操作。IoCancelIrp仅仅是简单地设置IRP的Cancel标志位然后调用IRP的取消例程。即:它并不知道你在是否修改过IRP指针,也不知道你是否正在处理这个IRP,所以它必须依靠一个你提供的取消例程来做大部分IRP取消工作。

关于取消IO更多的关于多CPU的讨论,见参考文献[1]。

[1] Windows驱动程序模型设计

posted @ 2011-01-05 17:37  浪里飞  阅读(2845)  评论(1编辑  收藏  举报