23、Windows派遣函数(1)-Windows驱动开发详解笔记,IRP
驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的。用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为一个叫做IRP的数据结构,不同的IRP数据会被“派遣”到不同的派遣函数(Dispatch Function)中。
1、IRP
IRP(I/O request packet)有两个属性,一个是MajorFunction,另外一个是MinorFunction。操作系统根据MajorFunction将IRP 派遣到不同的派遣函数中;在派遣函数中还可以继续判断IRP属于那种MinorFunction。
示例代码 P187
1 #pragma INITCODE
2 extern "C" NTSTATUS DriverEntry (
3 IN PDRIVER_OBJECT pDriverObject,
4 IN PUNICODE_STRING pRegistryPath )
5 {
6 NTSTATUS status;
7 KdPrint(("Enter DriverEntry\n"));
8
9 //设置卸载函数
10 pDriverObject->DriverUnload = HelloDDKUnload;
11
12 //设置派遣函数
13 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutin;
14 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutin;
15 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutin;
16 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutin;
17 pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = HelloDDKDispatchRoutin;
18 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloDDKDispatchRoutin;
19 pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = HelloDDKDispatchRoutin;
20 pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = HelloDDKDispatchRoutin;
21 pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HelloDDKDispatchRoutin;
22
23 //创建驱动设备对象
24 status = CreateDevice(pDriverObject);
25
26 KdPrint(("Leave DriverEntry\n"));
27 return status;
28 }
29 /************************************************************************
30 * 函数名称:HelloDDKDispatchRoutin
31 * 功能描述:对读IRP进行处理
32 * 参数列表:
33 pDevObj:功能设备对象
34 pIrp:从IO请求包
35 * 返回 值:返回状态
36 *************************************************************************/
37 #pragma PAGEDCODE
38 NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj,
39 IN PIRP pIrp)
40 {
41 KdPrint(("Enter HelloDDKDispatchRoutin\n"));
42
43 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
44 //建立一个字符串数组与IRP类型对应起来
45 static char* irpname[] =
46 {
47 "IRP_MJ_CREATE",
48 "IRP_MJ_CREATE_NAMED_PIPE",
49 "IRP_MJ_CLOSE",
50 "IRP_MJ_READ",
51 "IRP_MJ_WRITE",
52 "IRP_MJ_QUERY_INFORMATION",
53 "IRP_MJ_SET_INFORMATION",
54 "IRP_MJ_QUERY_EA",
55 "IRP_MJ_SET_EA",
56 "IRP_MJ_FLUSH_BUFFERS",
57 "IRP_MJ_QUERY_VOLUME_INFORMATION",
58 "IRP_MJ_SET_VOLUME_INFORMATION",
59 "IRP_MJ_DIRECTORY_CONTROL",
60 "IRP_MJ_FILE_SYSTEM_CONTROL",
61 "IRP_MJ_DEVICE_CONTROL",
62 "IRP_MJ_INTERNAL_DEVICE_CONTROL",
63 "IRP_MJ_SHUTDOWN",
64 "IRP_MJ_LOCK_CONTROL",
65 "IRP_MJ_CLEANUP",
66 "IRP_MJ_CREATE_MAILSLOT",
67 "IRP_MJ_QUERY_SECURITY",
68 "IRP_MJ_SET_SECURITY",
69 "IRP_MJ_POWER",
70 "IRP_MJ_SYSTEM_CONTROL",
71 "IRP_MJ_DEVICE_CHANGE",
72 "IRP_MJ_QUERY_QUOTA",
73 "IRP_MJ_SET_QUOTA",
74 "IRP_MJ_PNP",
75 };
76
77 UCHAR type = stack->MajorFunction;
78 if (type >= arraysize(irpname))
79 KdPrint((" - Unknown IRP, major type %X\n", type));
80 else
81 KdPrint(("\t%s\n", irpname[type]));
82
83
84 //对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
85 NTSTATUS status = STATUS_SUCCESS;
86 // 完成IRP
87 pIrp->IoStatus.Status = status;
88 pIrp->IoStatus.Information = 0; // bytes xfered
89 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
90
91 KdPrint(("Leave HelloDDKDispatchRoutin\n"));
92
93 return status;
94 }
在DriverEntry的驱动对象pDriverObject中,有个函数指针数组MajorFunction,系统默认这些IRP类型与IoInvalidateDeviceRequest函数关联。
IRP类型
http://www.cnblogs.com/mydomain/archive/2010/10/19/1855980.html
1)简单处理
将IRP状态设置成功,结束IRP请求,让派遣函数返回。
如上示例
#if (DRV_BOARD_TYPE == DRV_SCU)
if (strTrunk.tm[i] >= GESW_LOCAL_NET_MODID) /* 端口在奇槽位板 */
{
ucPortIdx += GESW_LOCAL_NET_PORT;
ucPort |= GESW_BOARD_IDX;
}
#endif
设备名只能被内核模式下看到,用户模式下用符号链接,IoCreateSymbolicLink来创建。
示例代码 P191
1 #include <windows.h>
2 #include <stdio.h>
3
4 int main()
5 {
6 HANDLE hDevice =
7 CreateFile("\\\\.\\HelloDDK",
8 GENERIC_READ | GENERIC_WRITE,
9 0, // share mode none
10 NULL, // no security
11 OPEN_EXISTING,
12 FILE_ATTRIBUTE_NORMAL,
13 NULL ); // no template
14
15 if (hDevice == INVALID_HANDLE_VALUE)
16 {
17 printf("Failed to obtain file handle to device: "
18 "%s with Win32 error code: %d\n",
19 "MyWDMDevice", GetLastError() );
20 return 1;
21 }
22
23 CloseHandle(hDevice);
24 return 0;
25 }
驱动对象会创建一个个的设备对象,并将这些设备对象“叠”成一个垂直结构,类似栈,称为“设备栈”。
IRP 会被操作系统发送到设备栈的顶层,如果顶层没有处理,则OS将IRP转发到设备栈的下一层设备处理,以此类推。为了记录IRP在每层所做的操作,IRP会有一个I/O Stack Locations数组,每个I/O Stack Locations元素记录着对应设备所做的操作;可以通过IoGetCurrentIrpStackLocation 来获得本层设备对应的I/O Stack Locations。
I/O Stack Locations可以参见MSDN中图示。用IRPTrace来跟踪IRP的调用情况。
2、缓冲区方式读写操作驱动程序所创建的设备的一般有三种读写方式:缓冲区方式,直接方式,其他方式;对应DO_BUFFERED_IO, DO_DIRECT_IO,and 0。
DO_BUFFERED_IO的意思是OS将应用程序提供缓冲区的数据复制到内核模式下的地址中来避免由于内核模式下进程切合切换而引起的读写错误问题。一般通过ReadFile,WriteFile来读写。优点是简单的解决了将用户地址地址传入驱动的问题,缺点是需要数据在两个模式下复制,影响效率。
在派遣函数中,通过IO_STACK_LOCATION中的ULONG ulReadLength = stack->Parameters.Read.Length;来知道ReadFile请求多少字节。再设置 pIrp->IoStatus.Information = ulReadLength;来记录设备实际操作了多少字节。
示例代码 P198
1 /************************************************************************
2 * 函数名称:DriverEntry
3 * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
4 * 参数列表:
5 pDriverObject:从I/O管理器中传进来的驱动对象
6 pRegistryPath:驱动程序在注册表的中的路径
7 * 返回 值:返回初始化驱动状态
8 *************************************************************************/
9 #pragma INITCODE
10 extern "C" NTSTATUS DriverEntry (
11 IN PDRIVER_OBJECT pDriverObject,
12 IN PUNICODE_STRING pRegistryPath )
13 {
14 NTSTATUS status;
15 KdPrint(("Enter DriverEntry\n"));
16
17 //设置卸载函数
18 pDriverObject->DriverUnload = HelloDDKUnload;
19
20 //设置派遣函数
21 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutin;
22 pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutin;
23 pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutin;
24 pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKRead;
25 pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = HelloDDKDispatchRoutin;
26 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloDDKDispatchRoutin;
27 pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = HelloDDKDispatchRoutin;
28 pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = HelloDDKDispatchRoutin;
29 pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HelloDDKDispatchRoutin;
30
31 //创建驱动设备对象
32 status = CreateDevice(pDriverObject);
33
34 KdPrint(("Leave DriverEntry\n"));
35 return status;
36 }
37
38 /************************************************************************
39 * 函数名称:CreateDevice
40 * 功能描述:初始化设备对象
41 * 参数列表:
42 pDriverObject:从I/O管理器中传进来的驱动对象
43 * 返回 值:返回初始化状态
44 *************************************************************************/
45 #pragma INITCODE
46 NTSTATUS CreateDevice (
47 IN PDRIVER_OBJECT pDriverObject)
48 {
49 NTSTATUS status;
50 PDEVICE_OBJECT pDevObj;
51 PDEVICE_EXTENSION pDevExt;
52
53 //创建设备名称
54 UNICODE_STRING devName;
55 RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");
56
57 //创建设备
58 status = IoCreateDevice( pDriverObject,
59 sizeof(DEVICE_EXTENSION),
60 &(UNICODE_STRING)devName,
61 FILE_DEVICE_UNKNOWN,
62 0, TRUE,
63 &pDevObj );
64 if (!NT_SUCCESS(status))
65 return status;
66
67 pDevObj->Flags |= DO_BUFFERED_IO;
68 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
69 pDevExt->pDevice = pDevObj;
70 pDevExt->ustrDeviceName = devName;
71 //创建符号链接
72 UNICODE_STRING symLinkName;
73 RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");
74 pDevExt->ustrSymLinkName = symLinkName;
75 status = IoCreateSymbolicLink( &symLinkName,&devName );
76 if (!NT_SUCCESS(status))
77 {
78 IoDeleteDevice( pDevObj );
79 return status;
80 }
81 return STATUS_SUCCESS;
82 }
83 /************************************************************************
84 * 函数名称:HelloDDKUnload
85 * 功能描述:负责驱动程序的卸载操作
86 * 参数列表:
87 pDriverObject:驱动对象
88 * 返回 值:返回状态
89 *************************************************************************/
90 #pragma PAGEDCODE
91 VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
92 {
93 PDEVICE_OBJECT pNextObj;
94 KdPrint(("Enter DriverUnload\n"));
95 pNextObj = pDriverObject->DeviceObject;
96 while (pNextObj != NULL)
97 {
98 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
99 pNextObj->DeviceExtension;
100
101 //删除符号链接
102 UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
103 IoDeleteSymbolicLink(&pLinkName);
104 pNextObj = pNextObj->NextDevice;
105 IoDeleteDevice( pDevExt->pDevice );
106 }
107 }
108 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
109 IN PIRP pIrp)
110 {
111 KdPrint(("Enter HelloDDKRead\n"));
112
113 //对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
114 NTSTATUS status = STATUS_SUCCESS;
115
116 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
117 ULONG ulReadLength = stack->Parameters.Read.Length;
118
119 // 完成IRP
120 //设置IRP完成状态
121 pIrp->IoStatus.Status = status;
122
123 //设置IRP操作了多少字节
124 pIrp->IoStatus.Information = ulReadLength; // bytes xfered
125
126 memset(pIrp->AssociatedIrp.SystemBuffer,0xAA,ulReadLength);
127
128 //处理IRP
129 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
130
131 KdPrint(("Leave HelloDDKRead\n"));
132
133 return status;
134 }
参考
【1】Windows 驱动开发技术详解
【2】http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx
【3】Windows驱动学习笔记,灰狐