能够保证各个并行的IRP顺序执行,即串行化。
很多时候,对设备的操作必须是串行化,驱动程序有必要将并行的请求变成串行的请求,需要用到队列。
并行运行(函数执行交织在一起)
如果想一次处理每个IRP,必须采用队列将处理串行化。采用“先来先服务”原则
typedef struct _KDEVICE_QUEUE { //IRP队列来实现串行 CSHORT Type; CSHORT Size; LIST_ENTRY devicelisthead; KSPIN_LOCK Lock; BOOLEAN Busy; } KDEVICE_QUEUE, *PKDEVICE_QUEUE, *RESTRICTED_POINTER PRKDEVICE_QUEUE;这个StartIO例程声明在DriverEntry中:
pDriverObject->DriverStartIo = HelloDDKStartIO;
运行再 DISPATCH_LEVEL 级别,不会被线程所打断。 这个例程 参数类似派遣函数,不敢没有返回值
在声明时 加上
#pragma LOCKEDCODE
示例:
在使用StartIO例程时,需要IRP的派遣函数返回挂起状态。调用
VOID IoStartPacket( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PULONG Key OPTIONAL, //If this is zero, the packet is inserted at the tail of the device queue. IN PDRIVER_CANCEL CancelFunction OPTIONAL //Specifies the entry point for a driver-supplied Cancel routine. );
VOID IoStartNextPacket( IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN Cancelable );
BOOLEAN KeRemoveEntryDeviceQueue( //从设备队列中将该IRP抽取出来 IN PKDEVICE_QUEUE DeviceQueue, IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry );
下面示例:
应用程序示例:
#include <windows.h> #include <stdio.h> #include <process.h> UINT WINAPI Thread(LPVOID context) { printf("Enter Thread "); //等待5秒 OVERLAPPED overlap={0}; overlap.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); UCHAR buffer[10]; ULONG ulRead; BOOL bRead = ReadFile(*(PHANDLE)context,buffer,10,&ulRead,&overlap); //可以试验取消例程 CancelIo(*(PHANDLE)context);//如果不注释这一句 那么会正常执行两个HelloDDKRead WaitForSingleObject(overlap.hEvent,INFINITE); printf("读取完毕! "); return 0; } int main() { HANDLE hDevice = CreateFile("\\.\HelloDDK", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED NULL ); if (hDevice == INVALID_HANDLE_VALUE) { printf("Open Device failed!"); return 1; } HANDLE hThread[2]; hThread[0] = (HANDLE) _beginthreadex (NULL,0,Thread,&hDevice,0,NULL); hThread[1] = (HANDLE) _beginthreadex (NULL,0,Thread,&hDevice,0,NULL); //主线程等待两个子线程结束 WaitForMultipleObjects(2,hThread,TRUE,INFINITE); printf("两个子线程结束! "); //创建IRP_MJ_CLEANUP IRP CloseHandle(hDevice); return 0; }
驱动代码:
#include "Driver.h" #pragma LOCKEDCODE VOID HelloDDKStartIO( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KIRQL oldirql; KdPrint(("Enter HelloDDKStartIO ")); //获取cancel自旋锁 IoAcquireCancelSpinLock(&oldirql); if (Irp!=DeviceObject->CurrentIrp||Irp->Cancel) { //如果当前有正在处理的IRP,则简单的入队列,并直接返回 //入队列的工作由系统完成,在StartIO中不用负责 KdPrint(("如果当前有正在处理的IRP,则简单的入队列,并直接返回 ")); IoReleaseCancelSpinLock(oldirql); KdPrint(("Leave HelloDDKStartIO ")); return; }else { //由于正在处理该IRP,所以不允许调用取消例程 //因此将此IRP的取消例程设置为NULL KdPrint(("由于正在处理该IRP,所以不允许调用取消例程 ")); IoSetCancelRoutine(Irp,NULL); IoReleaseCancelSpinLock(oldirql); } KEVENT event; KeInitializeEvent(&event,NotificationEvent,FALSE); //等3秒 KdPrint(("等3秒 ")); LARGE_INTEGER timeout; timeout.QuadPart = -3*1000*1000*10; //定义一个3秒的延时,主要是为了模拟该IRP操作需要大概3秒左右时间 KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,&timeout); //Specifies a Boolean value that is TRUE if the wait is alertable KdPrint(("等3秒结束 ")); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; // no bytes xfered IoCompleteRequest(Irp,IO_NO_INCREMENT); //在队列中读取一个IRP,并进行StartIo KdPrint(("在队列中读取一个IRP,并进行StartIo ")); IoStartNextPacket(DeviceObject,TRUE); //1)true 获取自旋锁 //2)删除设备队列 //3)获取IRP指针 //4)设置IRP //5)true 释放自旋锁 //6)调用StartIo函数 (设备对象,IRP) KdPrint(("Leave HelloDDKStartIO ")); } /************************************************************************ * 函数名称:DriverEntry * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 * 参数列表: pDriverObject:从I/O管理器中传进来的驱动对象 pRegistryPath:驱动程序在注册表的中的路径 * 返回 值:返回初始化驱动状态 *************************************************************************/ #pragma INITCODE extern "C" NTSTATUS DriverEntry ( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath ) { NTSTATUS status; KdPrint(("Enter DriverEntry ")); //设置卸载函数 pDriverObject->DriverUnload = HelloDDKUnload; //设置派遣函数 pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKRead; pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = HelloDDKDispatchRoutin; pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HelloDDKDispatchRoutin; //设置StartIO例程 pDriverObject->DriverStartIo = HelloDDKStartIO; //创建驱动设备对象 status = CreateDevice(pDriverObject); KdPrint(("Leave DriverEntry ")); return status; } /************************************************************************ * 函数名称:CreateDevice * 功能描述:初始化设备对象 * 参数列表: pDriverObject:从I/O管理器中传进来的驱动对象 * 返回 值:返回初始化状态 *************************************************************************/ #pragma INITCODE NTSTATUS CreateDevice ( IN PDRIVER_OBJECT pDriverObject) { NTSTATUS status; PDEVICE_OBJECT pDevObj; PDEVICE_EXTENSION pDevExt; //创建设备名称 UNICODE_STRING devName; RtlInitUnicodeString(&devName,L"\Device\MyDDKDevice"); //创建设备 status = IoCreateDevice( pDriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj ); if (!NT_SUCCESS(status)) return status; pDevObj->Flags |= DO_BUFFERED_IO; pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; pDevExt->pDevice = pDevObj; pDevExt->ustrDeviceName = devName; //创建符号链接 UNICODE_STRING symLinkName; RtlInitUnicodeString(&symLinkName,L"\??\HelloDDK"); pDevExt->ustrSymLinkName = symLinkName; status = IoCreateSymbolicLink( &symLinkName,&devName ); if (!NT_SUCCESS(status)) { IoDeleteDevice( pDevObj ); return status; } return STATUS_SUCCESS; } /************************************************************************ * 函数名称:HelloDDKUnload * 功能描述:负责驱动程序的卸载操作 * 参数列表: pDriverObject:驱动对象 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT pNextObj; KdPrint(("Enter DriverUnload ")); pNextObj = pDriverObject->DeviceObject; while (pNextObj != NULL) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pNextObj->DeviceExtension; //删除符号链接 UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName; IoDeleteSymbolicLink(&pLinkName); pNextObj = pNextObj->NextDevice; IoDeleteDevice( pDevExt->pDevice ); } } /************************************************************************ * 函数名称:HelloDDKDispatchRoutin * 功能描述:对读IRP进行处理 * 参数列表: pDevObj:功能设备对象 pIrp:从IO请求包 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("Enter HelloDDKDispatchRoutin ")); PIO_STACK_LOCATION stack = 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 type = stack->MajorFunction; if (type >= arraysize(irpname)) KdPrint((" - Unknown IRP, major type %X ", type)); else KdPrint((" %s ", irpname[type])); //对一般IRP的简单操作,后面会介绍对IRP更复杂的操作 NTSTATUS status = STATUS_SUCCESS; // 完成IRP pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("Leave HelloDDKDispatchRoutin ")); return status; } VOID OnCancelIRP( //如果在应用程序中调用CancelIo 取消例程 那么就会执行OnCancelIRP IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KdPrint(("Enter CancelReadIRP ")); if (Irp==DeviceObject->CurrentIrp) { //表明当前正在改由StartIo处理 KdPrint(("当前正在改由StartIo处理 ")); //但StartIo并没有获取cancel自旋锁之前 //这时候需要 KIRQL oldirql = Irp->CancelIrql; //释放Cancel自旋锁 IoReleaseCancelSpinLock(Irp->CancelIrql); IoStartNextPacket(DeviceObject,TRUE); KeLowerIrql(oldirql); }else { //从设备队列中将该IRP抽取出来 KdPrint(("从设备队列中将该IRP抽取出来 ")); KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,&Irp->Tail.Overlay.DeviceQueueEntry); //释放Cancel自旋锁 IoReleaseCancelSpinLock(Irp->CancelIrql); } //设置完成状态为STATUS_CANCELLED Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( Irp, IO_NO_INCREMENT ); KdPrint(("Leave CancelReadIRP ")); } NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { KdPrint(("Enter HelloDDKRead ")); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pDevObj->DeviceExtension; //将IRP设置为挂起 IoMarkIrpPending(pIrp); //将IRP插入系统的队列 IoStartPacket(pDevObj,pIrp,0,OnCancelIRP); //1)设置自旋锁 //2)设置取消例程 //3)设置IRP //4)释放自旋锁 //5)调用StartIO例程(设备对象,IRP) KdPrint(("Leave HelloDDKRead ")); //返回pending状态 return STATUS_PENDING; }