九、
初探IRP
对IRP的处理是驱动开发中很重要的一个部分,本章我们将简单介绍有关IRP的概念以及常规操作。
9.1 IRP的概念
此前我们可能曾经多次听说过IRP这个名词,那么它究竟是什么呢?
IRP的全名是I/O Request Package,即输入输出请求包,它是Windows内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求,操作系统将相应的I/O请求转换成相应的IRP,不同的IRP会根据类型被分派到不同的派遣例程中进行处理。
IRP有两个基本的属性,即MajorFunction和MinorFunction,分别记录IRP的主类型和子类型。操作系统根据MajorFunction决定将IRP分发到哪个派遣例程,然后派遣例程根据MinorFunction进行细分处理。
IRP的概念类似于Windows应用程序中“消息”的概念。在Win32编程中,程序由“消息”驱动,不同的消息被分发到不同的处理函数中,否则由系统默认处理。
文件I/O的相关函数例如CreateFile、ReadFile、WriteFile、CloseHandle等分
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
26
别会引发操作系统产生IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_CLOSE等不同的IRP,这些IRP会被传送到驱动程序的相应派遣例程中。
表9-1列出了常见的IRP类型并给出了简单说明:
表9-1 常见IRP类型
IRP类型
来源说明
IRP_MJ_CREATE
创建设备,CreateFile会产生此IRP
IRP_MJ_CLOSE
关闭设备,CloseHandle会产生此IRP
IRP_MJ_CLEANUP
清理,CloseHandle会产生此IRP
IRP_MJ_PNP
即插即用消息,NT式驱动不支持此IRP,只有WDM式驱动才支持
IRP_MJ_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
系统控制信息,类似于内核调用DeviceControl
IRP_MJ_WRITE
向设备写入数据,WriteFile会产生此IRP
9.2 IRP的处理
在第二章我们就介绍过如何在DriverEntry中为不同的IRP设置相应的派遣例程。在派遣例程中处理IRP最简单做法就是将IRP的状态设置为成功,然后结束IRP请求并返回成功,同时还要记得设置这个IRP请求操作了多少字节。
我们在派遣函数中设置IRP的完成状态为STATUS_SUCCESS,发起I/O请求的Win32 API才能返回TRUE,否则Win32API将返回FALSE,在这个时候可以通过GetLastError获得错误代码,这个错误代码会和此时IRP被设置的状态一
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
27
致。
下面的代码给出了简单的处理IRP例子:
NTSTATUS
TestDispatchRoutin(
INPDEVICE_OBJECT DeviceObject,
INPIRP Irp
)
{
NTSTATUSstatus=STATUS_SUCCESS;
// 设置IRP完成状态
Irp->IoStatus.Status=status;
// 设置IRP操作字节
Irp->IoStatus.Information=0;
// 结束IRP
IoCompleteRequest(Irp,IO_NO_INCREMENT);
returnstatus;
}
9.3 IRP派遣例程示例
在这个程序中,我们首先按照IRP_MJ_CLOSE的格式增加一个IRP_MJ_CLEANUP的派遣例程,具体请参考附文代码。
然后我们再编写一个应用层的控制台程序,代码如下所示:
#include"windows.h"
#include"stdio.h"
intmain()
{
// 打开设备句柄,它会触发IRP_MJ_CREATE
HANDLEhDevice=::CreateFile("\\\\.\\Test", // 符号链接
GENERIC_READ| GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hDevice==INVALID_HANDLE_VALUE)
{
printf("TrytoOpenDevice%sError:%d!\n","\\\\.\\Test",::GetLastError());
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
28
return-1;
}
// 关闭设备句柄,它会触发IRP_MJ_CLEANUP和IRP_MJ_CLOSE
CloseHandle(hDevice);
return0;
}
下面我们来介绍应用层程序打开的符号链接。我们查看驱动程序的DriverEntry,可以看到它调用IoCreateSymbolicLink创建了一个符号链接,如下所示:
//Test.h
#defineTEST_DOS_DEVICE_NAME_W L"\\DosDevices\\Test"
//Test.c
RtlInitUnicodeString(&dosDeviceName,TEST_DOS_DEVICE_NAME_W);
Status=IoCreateSymbolicLink(&dosDeviceName,&ntDeviceName);
从上面可以看出该驱动的符号链接名为“\DosDevices\Test”,也可以写成“\??\Test”,但在编程的时候需要稍微改动一下,写成“\\.\Test”。
现在我们使用KmdManager加载驱动并运行,同时运行我们前面编写的应用层驱动程序,根据应用层程序的代码我们可知它应该会触发IRP_MJ_CREATE、IRP_MJ_CLEANUP、IRP_MJ_CLOSE这三个IRP,而我们的验证结果如图9-2所示:
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
图9-1 程序演示结果
从图9-1中可以看出在应用层程序中调用CreateFile等函数确实会产生相应的IRP,同时我们也理解了如何在应用层打开驱动设备。
初探IRP
对IRP的处理是驱动开发中很重要的一个部分,本章我们将简单介绍有关IRP的概念以及常规操作。
9.1 IRP的概念
此前我们可能曾经多次听说过IRP这个名词,那么它究竟是什么呢?
IRP的全名是I/O Request Package,即输入输出请求包,它是Windows内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求,操作系统将相应的I/O请求转换成相应的IRP,不同的IRP会根据类型被分派到不同的派遣例程中进行处理。
IRP有两个基本的属性,即MajorFunction和MinorFunction,分别记录IRP的主类型和子类型。操作系统根据MajorFunction决定将IRP分发到哪个派遣例程,然后派遣例程根据MinorFunction进行细分处理。
IRP的概念类似于Windows应用程序中“消息”的概念。在Win32编程中,程序由“消息”驱动,不同的消息被分发到不同的处理函数中,否则由系统默认处理。
文件I/O的相关函数例如CreateFile、ReadFile、WriteFile、CloseHandle等分
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
26
别会引发操作系统产生IRP_MJ_CREATE、IRP_MJ_READ、IRP_MJ_WRITE、IRP_MJ_CLOSE等不同的IRP,这些IRP会被传送到驱动程序的相应派遣例程中。
表9-1列出了常见的IRP类型并给出了简单说明:
表9-1 常见IRP类型
IRP类型
来源说明
IRP_MJ_CREATE
创建设备,CreateFile会产生此IRP
IRP_MJ_CLOSE
关闭设备,CloseHandle会产生此IRP
IRP_MJ_CLEANUP
清理,CloseHandle会产生此IRP
IRP_MJ_PNP
即插即用消息,NT式驱动不支持此IRP,只有WDM式驱动才支持
IRP_MJ_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
系统控制信息,类似于内核调用DeviceControl
IRP_MJ_WRITE
向设备写入数据,WriteFile会产生此IRP
9.2 IRP的处理
在第二章我们就介绍过如何在DriverEntry中为不同的IRP设置相应的派遣例程。在派遣例程中处理IRP最简单做法就是将IRP的状态设置为成功,然后结束IRP请求并返回成功,同时还要记得设置这个IRP请求操作了多少字节。
我们在派遣函数中设置IRP的完成状态为STATUS_SUCCESS,发起I/O请求的Win32 API才能返回TRUE,否则Win32API将返回FALSE,在这个时候可以通过GetLastError获得错误代码,这个错误代码会和此时IRP被设置的状态一
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
27
致。
下面的代码给出了简单的处理IRP例子:
NTSTATUS
TestDispatchRoutin(
INPDEVICE_OBJECT DeviceObject,
INPIRP Irp
)
{
NTSTATUSstatus=STATUS_SUCCESS;
// 设置IRP完成状态
Irp->IoStatus.Status=status;
// 设置IRP操作字节
Irp->IoStatus.Information=0;
// 结束IRP
IoCompleteRequest(Irp,IO_NO_INCREMENT);
returnstatus;
}
9.3 IRP派遣例程示例
在这个程序中,我们首先按照IRP_MJ_CLOSE的格式增加一个IRP_MJ_CLEANUP的派遣例程,具体请参考附文代码。
然后我们再编写一个应用层的控制台程序,代码如下所示:
#include"windows.h"
#include"stdio.h"
intmain()
{
// 打开设备句柄,它会触发IRP_MJ_CREATE
HANDLEhDevice=::CreateFile("\\\\.\\Test", // 符号链接
GENERIC_READ| GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(hDevice==INVALID_HANDLE_VALUE)
{
printf("TrytoOpenDevice%sError:%d!\n","\\\\.\\Test",::GetLastError());
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
28
return-1;
}
// 关闭设备句柄,它会触发IRP_MJ_CLEANUP和IRP_MJ_CLOSE
CloseHandle(hDevice);
return0;
}
下面我们来介绍应用层程序打开的符号链接。我们查看驱动程序的DriverEntry,可以看到它调用IoCreateSymbolicLink创建了一个符号链接,如下所示:
//Test.h
#defineTEST_DOS_DEVICE_NAME_W L"\\DosDevices\\Test"
//Test.c
RtlInitUnicodeString(&dosDeviceName,TEST_DOS_DEVICE_NAME_W);
Status=IoCreateSymbolicLink(&dosDeviceName,&ntDeviceName);
从上面可以看出该驱动的符号链接名为“\DosDevices\Test”,也可以写成“\??\Test”,但在编程的时候需要稍微改动一下,写成“\\.\Test”。
现在我们使用KmdManager加载驱动并运行,同时运行我们前面编写的应用层驱动程序,根据应用层程序的代码我们可知它应该会触发IRP_MJ_CREATE、IRP_MJ_CLEANUP、IRP_MJ_CLOSE这三个IRP,而我们的验证结果如图9-2所示:
Windows 下设备驱动程序的开发方法 2120080411 计算机应用 赖锡盛
图9-1 程序演示结果
从图9-1中可以看出在应用层程序中调用CreateFile等函数确实会产生相应的IRP,同时我们也理解了如何在应用层打开驱动设备。