驱动开发(13)IRP 的异步完成和 CancelRoutine
来源:互联网 发布:刷机救砖软件哪个好 编辑:程序博客网 时间:2024/05/17 23:05
本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/51301922
在之前的博文中,我们对于IRP,都是同步完成的,但是 Windows 对异步操作很友好,我们来看看如何异步完成 IRP 。
在应用程序中异步访问设备
在开始之前,我认为有必要提一句异步访问设备。在之前的博文中,与驱动通信的代码都是采用同步访问设备的,其实所谓同步访问,是 Win32 子系统封装了“等待”这一过程。 Win32API 会在内部创建事件,并在向设备发送 I/O 请求后直接等待事件被完成,一旦驱动程序完成 I/O ,那么就会激活事件,从而使 Win32API 中的等待状态结束,线程恢复运行。(关于“事件”对象,见上一篇博文“内核中开启多线程和同步对象”)
其实,这个等待操作我们可以自己来做,就像这样:
#include "stdafx.h"#include<Windows.h>int _tmain(int argc, _TCHAR* argv[]){ //如果 CreateFile 的第 6 个参数 dwFlagsAndAttributes 被指定为 FILE_FLAG_OVERLAPPED, HANDLE handle = CreateFile(TEXT("\\\\.\\D:\\1.txt"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE){ MessageBoxA(0, "打开文件/设备失败", "错误", 0); return 0; } unsigned char buffer[50] = { 0 }; OVERLAPPED over = { 0 }; HANDLE Event = CreateEvent(NULL, FALSE, FALSE, NULL); over.hEvent = Event; ReadFile(handle, buffer, 49, NULL, &over); //Do somethings WaitForSingleObject(Event, INFINITE); for (int i = 0; i < sizeof(buffer); i++) printf("0x%X ", buffer[i]); CloseHandle(handle); getchar(); return 0;}
当然,还有一种方法异步访问设备,即使用ReadFileEx/WriteFileEx,这是通过APC来实现异步访问的,这里不展开了。
为什么要说异步访问设备呢,这是为了避免大家和下面的异步完成IRP产生混淆,同时也有必要让大家明白下 Win32 子系统对同步读写设备的实现,同时,了解异步访问设备有助于下面的对派遣函数和IRP相关内容的理解。
要异步访问设备,需要得到驱动程序的支持,当应用程序调用 I/O 函数时,驱动程序的 Dispatch Function 会被调用,当 Dispatch Function 返回时,应用程序的 I/O 函数才能退出。驱动程序调用 IoMarkIrpPending 并返回 STATUS_PENDING 时,意味着 I/O 请求已经挂起,如果此时应用程序选择了异步访问设备,那么 I/O 函数会退出,当 I/O 真正处理完成时(即调用 IoCompleteRequest ),应用程序创建的 Event 就会被激发!WaitForSingleObject 会返回。而同步访问,其实是在 I/O 函数内部创建并等待了这个事件。
异步完成 IRP
要异步完成IRP,需要在派遣函数中不调用 IoCompleteRequest ,而是调用 IoMarkIrpPending 函数,同时需要派遣函数返回 STATUS_PENDING
通过上面的异步访问设备我们可以发现,应用程序会创建一个 Event 对象,要使应用程序等待完成,需要激活这个事件,这个过程不需要驱动程序自己去做的,完成IRP时如果调用 IoCompleteRequest , IoCompleteRequest 会自动激活此事件。
也就是说,调用 IoMarkIrpPending 后,应用程序创建的事件并没有被激活,即,如果应用程序等待此事件(如同步读写),并不会使其退出等待,他的作用仅仅是使派遣函数返回,以便于驱动程序在其他地方完成 IRP 。
就像这样,之后,此 IRP 被挂起,驱动程序可以保存此 IRP 的指针,在未来某个必要的时刻完成他:
//in a Dispatch FunctionIoMarkIrpPending(pIrp);return STATUS_PENDING;
CancelRoutine 取消 I/O 例程
有些时候,驱动程序需要允许应用程序“取消”某个 I/O 请求。比如,应用程序程序异步读写文件时可以实现一个“终止”按钮。这需要驱动程序的支持。如果我们希望给自己的设备实现这种功能,就需要给 IRP 设置取消例程。应用程序通过 Win32 子系统提供的 CancelIO API来取消一个 I/O 请求,这会调用驱动程序设置的取消例程。
设置取消例程的内核函数是 IoSetCancelRoutine,这个函数的原型如下:
PDRIVER_CANCEL IoSetCancelRoutine( _In_ PIRP Irp, _In_ PDRIVER_CANCEL CancelRoutine);
- 参数1是要设置取消例程的 IRP 指针。
- 参数2是取消例程指针。如果此参数为 NULL ,则删除取消例程
返回值:当前 IRP 存在取消例程时返回 Irp->CancelRoutine ,否则返回 NULL 。
取消例程的原型如下:
DRIVER_CANCEL Cancel;VOID Cancel( _Inout_ struct _DEVICE_OBJECT *DeviceObject, _Inout_ struct _IRP *Irp){ ... }
IoCancelIrp 会在内部调用 IoAcquireCancelSpinLock 获取cancel自旋锁,因此,在取消例程中,必须调用 IoReleaseCancelSpinLock 函数释放自旋锁,否则会导致蓝屏死机。
IRP 异步完成和 CancelRoutine 例程代码
最后,我们以一个例程来结束本篇博文,此例中,我们在处理”读”的派遣函数中挂起 IRP 来演示 IRP 异步完成,并设置 CancelRoutine ,并在取消例程中将挂起的 IRP 完成。
应用程序源码:
#include "stdafx.h"#include<Windows.h>int _tmain(int argc, _TCHAR* argv[]){ //打开设备 HANDLE handle = CreateFileA("\\\\.\\MyDevice1_link", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE){ MessageBoxA(0, "打开设备失败", "错误", 0); return 0; } unsigned char buffer[50] = { 0 }; DWORD len; OVERLAPPED over = { 0 }; HANDLE Event = CreateEvent(NULL, FALSE, FALSE, NULL); over.hEvent = Event; if (!ReadFile(handle, buffer, 49, &len, &over)){ if (GetLastError() == ERROR_IO_PENDING){ puts("I/O is Pending"); } } Sleep(3000); CancelIo(handle); CloseHandle(handle); return 0;}
驱动程序源码:
#include <ntddk.h>extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject);extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);extern "C" NTSTATUS ReadDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);//我们定义的设备扩展typedef struct _DEVICE_EXTENSION { UNICODE_STRING SymLinkName;//符号链接名} DEVICE_EXTENSION, *PDEVICE_EXTENSION;#pragma code_seg("INIT")extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath){ DbgPrint("DriverEntry\r\n"); pDriverObject->DriverUnload = DriverUnload;//注册驱动卸载函数 //注册派遣函数 pDriverObject->MajorFunction[IRP_MJ_CREATE] = DefDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DefDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_WRITE] = DefDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_READ] = ReadDispatchRoutine; NTSTATUS status; PDEVICE_OBJECT pDevObj; PDEVICE_EXTENSION pDevExt; //创建设备名称的字符串 UNICODE_STRING devName; RtlInitUnicodeString(&devName, L"\\Device\\MyDevice1"); //创建设备 status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj); if (!NT_SUCCESS(status)) return status; pDevObj->Flags |= DO_BUFFERED_IO;//将设备设置为缓冲设备 pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展 //创建符号链接 UNICODE_STRING symLinkName; RtlInitUnicodeString(&symLinkName, L"\\??\\MyDevice1_link"); pDevExt->SymLinkName = symLinkName; status = IoCreateSymbolicLink(&symLinkName, &devName); if (!NT_SUCCESS(status)) { IoDeleteDevice(pDevObj); return status; } return STATUS_SUCCESS;}extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject){ DbgPrint("DriverUnload\r\n"); PDEVICE_OBJECT pDevObj; pDevObj = pDriverObject->DeviceObject; PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展 //删除符号链接 UNICODE_STRING pLinkName = pDevExt->SymLinkName; IoDeleteSymbolicLink(&pLinkName); //删除设备 IoDeleteDevice(pDevObj);}extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp){ DbgPrint("DefDispatchRoutine\r\n"); NTSTATUS status = STATUS_SUCCESS; pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status;}VOID Read_CancelIRP(PDEVICE_OBJECT DeviceObject, PIRP pIrp){ DbgPrint("Read_CancelIRP pIrp: 0x%X\r\n", pIrp); //完成状态设置为 STATUS_CANCELLED pIrp->IoStatus.Status = STATUS_CANCELLED; //操作字节数 pIrp->IoStatus.Information = 0; //完成 IRP IoCompleteRequest(pIrp, IO_NO_INCREMENT); //释放 Cancel 自旋锁 IoReleaseCancelSpinLock(pIrp->CancelIrql);}extern "C" NTSTATUS ReadDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp){ DbgPrint("ReadDispatchRoutine\r\n"); NTSTATUS status = STATUS_SUCCESS; //得到设备扩展 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; //得到I/O堆栈的当前这一层,也就是IO_STACK_LOCATION结构的指针 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); //ULONG ReadLength = stack->Parameters.Read.Length;//得到读的长度 //ULONG ReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart;//得到读偏移量 //DbgPrint("ReadLength: %d\r\nReadOffset: %d\r\n", ReadLength, ReadOffset);//输出相关信息 //PVOID buffer = pIrp->AssociatedIrp.SystemBuffer;//得到缓冲区指针 //if (ReadOffset + ReadLength > BUFFER_LENGTH){ // //如果要操作的超出了缓冲区,则失败完成IRP,返回无效 // DbgPrint("E: The size of the data is too long.\r\n"); // status = STATUS_FILE_INVALID;//会设置用户模式下的GetLastError // ReadLength = 0;//设置操作了0字节 //} //else{ // //没有超出,则进行缓冲区复制 // DbgPrint("OK, I will copy the buffer.\r\n"); // RtlMoveMemory(buffer, pDevExt->buffer + ReadOffset, ReadLength); // status = STATUS_SUCCESS; //} IoSetCancelRoutine(pIrp, Read_CancelIRP); IoMarkIrpPending(pIrp); DbgPrint("IoMarkIrpPending pIrp: 0x%X\r\n", pIrp); return STATUS_PENDING; //pIrp->IoStatus.Status = status;//设置IRP完成状态,会设置用户模式下的GetLastError //pIrp->IoStatus.Information = ReadLength;//设置操作字节数 //IoCompleteRequest(pIrp, IO_NO_INCREMENT);//完成IRP //return status;}
效果图:
- 驱动开发(13)IRP 的异步完成和 CancelRoutine
- IRP的异步完成
- IRP的同步完成与异步完成
- IRP的同步完成与异步完成
- 驱动开发之 完成IRP的几种情况
- Windows驱动开发WDM (7)- 异步IRP
- Windows驱动开发WDM (16)- 完成例程 (重新获得IRP控制权)
- irp的异步完成_irp等待插入队列_删除队列_结束irp
- irp的异步完成_取消irp设置回调函数_应用层CancelIo
- 驱动开发程序的IRP处理过程
- 分层驱动模型中IRP的传递与完成
- 分层驱动模型中IRP的传递与完成
- IRP的完成例程
- IRP的简单完成例程!
- IRP的传递与完成
- Irp的传递与完成
- 驱动开发之 常见IRP
- 同步,异步IRP的处理
- swift开发多线程篇 - 多线程基础
- jq垂直下拉菜单
- Android 存储记录
- oracle的触发器
- Android_前台service_被kill
- 驱动开发(13)IRP 的异步完成和 CancelRoutine
- 第k个数
- activiti源码编译
- iOS中摄像头、相册获取图片,压缩图片,上传服务器方法
- nil、NIL以及null间的联系和区别
- current_next.php
- 5.1.加油.
- leetcode 13. Roman To Integer
- 奋斗吧,程序员——第二十三章 正是江南好风景,落花时节又逢君