在写第一个驱动程序时,在DriverEntry、HelloDDKUnload、CreateDevice和HelloDDKDispatchRoutine这四个函数中,对前三个函数都有了一些了解,但对HelloDDKDispatchRoutine这个函数的作用却是一无所知,现在已经知道了,这是一个派遣函数。
派遣函数是驱动程序中的核心部分,所以我首先想弄清IRP与派遣函数的关系。
一、IRP的概念
想要用一个菜鸟的语言来描述这些复杂的技术术语,难免会有一些偏差。IRP(I/O Request Package)是什么?从字面上来理解,IRP就是一个IO请求包,包含了应用层进行IO请求的相关数据。
IRP的类型很多,有IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_CLOSE。比如CreateFile会产生一个IRP_MJ_CREATE的IRP,ReadFile会产生一个IRP_MJ_READ的IRP。
这些产生的IPR会被传送到不同的派遣函数中,所以照现在的理解来看,IRP只是一个数据的中转分发体。
对于IRP,只需要理解其概念就行了,而真正的实现是在自定义的派遣函数中,这也是在DriverEntry中PDRIVER_OBJECT结构体成员产MajorFunction所指定的函数。
二、派遣函数
对于派遣函数的最简单的处理,就是设置IRP的状态为成功,然后结束IRP请求,并让派遣函数成功返回。
在第一个驱动程序中,只有一个派遣函数,而该函数什么也没做,只进行了最简单的处理,来看看代码:
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine( IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp )
{
KdPrint( ( "Enter HelloDDKDispatchRoutine!/n" ) );
NTSTATUS status = STATUS_SUCCESS;
//完成IRP
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint( ( "Leave HelloDDKDispatchRoutine!/n" ) );
return status;
}
在这个函数中,有一个PIRP的结构体,在这个函数中的所有操作对象都是这个PIRP结构体。其中“pIrp->IoStatus.Status”设置IRP完成状态为“成功”;“PIrp->IoStatus.Information”设置IRP操作了多少字节;最后用IoCompleteRequest结构IRP请求并返回。
对于IoCompleteRequest这个函数,有必要作个解释。在《Windows驱动开发详解》一书中对这个函数的功能作了很好且详细的解释。现在再用我自己的理解来对这个函数的作用作一描述:
先谈谈Win32 API中的WriteFile函数,对于Win32程序员,使用这个函数很简单,使用WriteFile函数向文件中写入内容,然后Win32程序的当前线程会进入“睡眠”模式,等待WriteFile函数返回,再继续之后的操作。而IoCompleteReques的作用就是将将“睡眠”的线程恢复运行。
理解了IoCompleteRequest的作用,那现在就可以来看看IoCompleteRequest函数的第2个参数的作用了:
VOID
IoCompleteRequest(
IN PIRP Irp,
IN CCHAR PriorityBoost
);
第一个参数irp是当前派遣函数中的IRP数据,在HelloDDKDispatchRoutine函数中只设置了IRP的返回状态及写入的字节数,或许还可以对IRP进行更多的操作。那第二个参数是作什么用的呢?
priorityBoost其实只是设置“睡眠”线程在恢复运行后的优先级,由于我以前主要使用的是系统中的应用,从来没进行硬件设备的操作,所以对这个功能的作用理解得不深。如果不需要增加线程的优先级的话,那就直接将priorityBoost设置为IO_NOINCREMENT,不增加优先级。
对于派遣函数的更多应用,我想还要更多的笔记中详细记录才行。不过,至少现在已经明白了什么是IRP,派遣函数的作用又是什么了。