转载自;http://mzf2008.blog.163.com/blog/static/3559978620101114103859212/
IRP与派遣函数< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
IRP的处理机制类似Windows应用程序的“消息处理”机制,驱动程序接收到不同类型的IRP后,会进入不同的派遣函数,在派遣函数中IRP得到处理。
IRP
在Windows内核中,有一种数据结构叫做IPR(I/O Request Package),即输入输出请求包。它是与输入输出相关的重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求包。操作系统将I/O请求转化为IRP数据,不用类型的IRP会根据类型传递到不同的派遣函数中。
IRP是一个很复杂的数据结构。其中两个很重要的属性MajorFunction和MinorFunction,分别记录IRP的主类型和子类型。操作系统根据MajorFunction将IRP“派遣”到不同的派遣函数中,在派遣函数中还可以继续判断这个IRP属于哪种MinorFunction。
一般来说,NT式驱动程序和WDM驱动程序都是在DriverEntry中注册IRP的派遣函数。
如下代码:
#pragma INITCODE
extern "C" NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObj,
IN PUNICODE_STRING pRegisterPath )
{
KdPrint(("Entry DriverEntry!\n"));
//设置卸载函数
pDriverObj->DriverUnload = HelloDDKUnload;
//设置派遣函数
pDriverObj->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
pDriverObj->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
NTSTATUS status;
//创建设备对象
status = CreateDevice(pDriverObj);
KdPrint(("Leave DriverEntry!\n"));
return STATUS_SUCCESS;
}
在DriverEntry的驱动对象pDriverObj中,有个函数指针数组MajorFunction,这个数据的每个元素都记录着一个函数的地址。通过设置这个数组,可以将IRP的类型和派遣函数关联起来。
在上面的例子中,只设置了4种类型的IRP派遣函数。对于其他没有设置的IRP类型,系统默认这些IRP类型与_IopInvalidDeviceRequest函数关联。
在进入DriverEntry之前,操作系统会将_IopInvalidDeviceRequest的地址填满整个MajorFunction数组。
IRP类型
IRP类型 | 来源 |
IRP_MJ_CREATE | 创建设备,CreateFile会产生此IRP |
IRP_MJ_CLOSE | 关闭设备,CloseHandle会产生此IRP |
IRP_MJ_CLEARUP | 清除工作,CloseHandle会产生此IRP |
IRP_MJ_DEVICE_CONTROL | DeviceIoControl函数会产生此IRP |
IRP_MJ_PNP | 即插即用消息,只有WDM才支持此种IRP |
IRP_MJ_PNP_POWER | 在操作系统处理电源消息时产生此IRP |
IRP_MJ_QUERY _INFORMATION | 获取文件长度,GetFileSize会产生此IRP |
IRP_MJ_READ | 读取设备内容,ReadFIle会产生此IRP |
IRP_MJ_SET_INFORMATION | 设置文件长度,GetFileSize会产生此IRP |
IRP_MJ_SHUTDOWN | 关闭系统前会产生此IRP |
IRP_MJ_SYSTEM_CONTROL | 系统内部产生的控制信息 |
IRP_MJ_WRITE | 对设备进行WriteFile时产生此IRP |
对派遣函数的简单处理
如下代码:
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKDispatchRoutine\n"));
NTSTATUS status = STATUS_SUCCESS;
// 设置IRP完成状态为STATUS_SUCCESS,这样,发起I/O请求的API函数(如WriteFile)
//将会返回TRUE;
pIrp->IoStatus.Status = status;
//设置IRP操作了多少字节,如果是ReadFile产生的IRP,这个字节数代表从设备中读
//了多少字节。
pIrp->IoStatus.Information = 0; // bytes xfered
// IoCompleteRequest
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKDispatchRoutine\n"));
return status;
}
应用程序通过符号链接打开设备
在应用程序中,设备可以通过符号链接进行访问。在驱动程序中,我们通过函数IoCreateSymbolicLink来创建符号链接。加入我们定义的符号链接为\??\HelloDevice,那么我们在应用程序中应该把前面的\??\改写成\\.\,即\\.\HelloDevice,写成C语言字符串形式则为\\\\.\\HelloDevice
示例代码:(注:每个IRP对应着自己相应的派遣函数)
int main(void)
{
HANDLE hDevice;
//打开设备句柄,触发IRP_MJ_CREATE
hDevice = CreateFile("\\\\.\\HelloDDK",GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
printf("%d\n", dwError);
}
//关闭设备句柄,触发IRP_MJ_CLEANUP 和IRP_MJ_CLOSE
CloseHandle(hDevice);
return 1;
}

编写一个更通用的派遣函数
首先介绍一个重要的数据结构IO_STACK_LOCATION,即I/O堆栈,这个数据结构跟IRP紧密相连。
我们知道,驱动对象会创建一个个的设备对象,并将这些设备对象叠成一个垂直结构。这种垂直的结构很像栈,因此被称为“设备栈”。
IRP会被操作系统发送到设备栈的顶层,如果顶层的设备对象的派遣函数结束了IRP的请求,则这次I/O请求结束。如果没有将IRP的请求结束,那么操作系统会将IRP转发到设备栈的下一层设备进行处理。如果这个设备的派遣函数依然不能结束IRP请求,那么则会继续向下层设备转发。
因此,一个IRP可能会被转发多次。为了记录IRP在每层设备中做的操作。IRP会有一个IO_STACK_LOCATION数组。数组 的元素数应该大于IRP穿越的设备数。每个IO_STACK_LACTION元素记录着对应设备中做的操作。对于本层设备对应 的IO_STACK_LOCATION,可以通过IoGetCurrentIrpStackLocation函数得到。
PIO_STACK_LOCATION
IoGetCurrentIrpStackLocation(
IN PIRP Irp
);
注意:IO_STACK_LOCATION结构中的MajorFunction子域会记录当前IRP的类型。
下面的代码演示了派遣函数如何获得当前的IO_STACK_LOCATION,以及如何获得IRP的类型。
示例代码:(注:每个IRP对应着同一个派遣函数)
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDeviceObj, IN PIRP pIrp)
{
KdPrint(("进入派遣函数中!\n"));
//获取当前设备中的IO_STACK_LOCATION结构
PIO_STACK_LOCATION pIos;
pIos = IoGetCurrentIrpStackLocation(pIrp);
//建立一个字符串数组,与IRP类型对应起来
static CHAR* irpName[] = {
"IRP_MJ_CREATE",
"IRP_MJ_CREATE_NAMED_PIPE",
"IRP_MJ_CLOSE",
"IRP_MJ_READ",
"IRP_MJ_WRITE",
"IRP_MJ_QUERY_INFORMATION",
"IRP_MJ_SET_INFORMATION",
"IRP_MJ_QUERY_EA",
"IRP_MJ_SET_EA",
"IRP_MJ_FLUSH_BUFFERS",
"IRP_MJ_QUERY_VOLUME_INFORMATION",
"IRP_MJ_SET_VOLUME_INFORMATION",
"IRP_MJ_DIRECTORY_CONTROL",
"IRP_MJ_FILE_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CONTROL",
"IRP_MJ_INTERNAL_DEVICE_CONTROL",
"IRP_MJ_SHUTDOWN",
"IRP_MJ_LOCK_CONTROL",
"IRP_MJ_CLEANUP",
"IRP_MJ_CREATE_MAILSLOT",
"IRP_MJ_QUERY_SECURITY",
"IRP_MJ_SET_SECURITY",
"IRP_MJ_POWER",
"IRP_MJ_SYSTEM_CONTROL",
"IRP_MJ_DEVICE_CHANGE",
"IRP_MJ_QUERY_QUOTA",
"IRP_MJ_SET_QUOTA",
"IRP_MJ_PNP"
};
UCHAR irpType = pIos->MajorFunction;
if (irpType >= arraysize(irpName))
{
KdPrint(("不明真相的类型,类型为:%x", irpType));
}
else
{
KdPrint(("%s\n", irpName[irpType]));
}
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("离开派遣函数!\n"));
return status;
}
