IO_REMOVE_LOCK使用方法小结(转载加改正)
原文链接:http://www.programlife.net/io_remove_lock.html
IO_REMOVE_LOCK(删除锁)的具体结构没有公开,WDK的文档中中查不到IO_REMOVE_LOCK。最开始看到IO_REMOVE_LOCK是在WDK的例子event中。下面是参考网上的一些资料之后的一点总结,错误的地方请指正。新增内容:WDK8.1 中是可以看到这个结构的,在wdm.h中。
为什么要用IO_REMOVE_LOCK? WDM 驱动程序在处理设备删除 IRP 并释放驱动程序分配的内存后可能接收到附加的 IRP。在处理附加的 IRP 时试图引用已经释放的内存会导致系统崩溃。驱动程序能够接收已删除设备的 IRP,这有两个原因:
1。在设备被删除后,另一个组件可以发送 I/O。要发送一个 IRP,组件获取目标设备或文件的指针并去除该设备对象上的引用(或者 I/O 管理器代表组件去除引用)。引用可以确保目标设备或文件对象的持续性,从而目标驱动程序可以访问设备对象和设备扩展。但是,除非组件已经在目标设备上注册了即插即用通知,否则它不能确定设备是否仍然存在。
2。在设备删除请求之前发送的 I/O 请求可能在目标驱动程序处理设备删除请求之后到达。这种情况是否发生取决于哪个组件在发送 I/O、目标驱动程序在设备堆栈中的位置以及为设备挂起的其他 I/O 请求。 通俗一点的解释:有时候I/O管理器发出的PnP请求会与其它I/O请求(如包含读写的请求)同时出现。这完全有可能,例如当你处理其它IRP时收到了IRP_MN_REMOVE_DEVICE请求。你必须自己避免这种麻烦产生,标准的做法是使用一个IO_REMOVE_LOCK对象和几个相关的内核模式支持例程。
防止设备被过早地删除的基本想法是在每一次开始处理请求时都获取删除锁,处理完成后释放删除锁。在你删除你的设备对象前,应确保删除锁未被使用。否则,你将等到这个锁的所有引用都被释放。下图显示了这个过程: IO_REMOVE_LOCK怎么用,IO_REMOVE_LOCK是什么

怎么使用IO_REMOVE_LOCK? 在驱动程序的设备扩展(DEVICE_EXTENSION)中定义IO_REMOVE_LOCK类型的变量,并在 AddDevice例程中调用IoInitializeRemoveLock对其进行初始化。
此后,无论何时,当你收到一个I/O请求时(除了IRP_MJ_CREATE),你就调用IoAcquireRemoveLock。如果删除设备的操作正在进行,则IoAcquireRemoveLock返回STATUS_DELETE_PENDING。否则,该函数将获得删除锁并返回STATUS_SUCCESS。一旦你完成一个I/O操作,就调用IoReleaseRemoveLock,该函数将释放删除锁以及目前未处理的删除操作。
当处理一个设备删除请求 (IRP_MN_REMOVE_DEVICE) 时,驱动程序通过调用 IoReleaseRemoveLockAndWait 来释放在其 DispatchPnP 例程中获取的删除锁。这个调用直到与删除锁关联的引用计数达到零时才返回,这表示删除锁的所有其他持有者都已经被释放。在 IoReleaseRemoveLockAndWait 返回之后,驱动程序将 IRP 沿其设备堆栈向下传递(如有必要),调用 IoDetachDevice 来从设备堆栈中删除其设备对象,然后释放在其 AddDevice 例程中分配的资源(例如池内存)。最后,驱动程序调用 IoDeleteDevice 来标记要删除的设备对象。
何时调用IoReleaseRemoveLock? 驱动程序何时应该调用 IoReleaseRemoveLock 取决于它如何处理IRP:通过将其传递给下一层驱动程序(不设置完成例程)、通过完成 IRP 而不将其传递给下一层驱动程序或者通过向下传递 IRP 并设置一个完成例程。
如果驱动程序将 IRP 传递给下一层驱动程序并且不设置 IoCompletion 例程,那么驱动程序应该在调用 IoCallDriver 之后调用 IoReleaseRemoveLock 来向下传递 IRP。
NTSTATUS MyDispatchRoutine (
IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION devExt;
NTSTATUS status;
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
status = IoAcquireRemoveLock (&devExt->RemoveLock, Irp); if (!NT_SUCCESS (status)) {
// maybe device is being removed.
Irp->IoStatus.Information = ;
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
} // Do request-specific processing . . . // Pass down the IRP and release the lock. IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt->NextLowerDriver, Irp);
IoReleaseRemoveLock (&devExt->RemoveLock, Irp);
return status;
}
如果驱动程序完成 IRP 并且不将其传递给下一层驱动程序,那么驱动程序应该在调用 IoCompleteRequest 之后调用 IoReleaseRemoveLock,如下例所示:
NTSTATUS MyDispatchRoutine (
IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION devExt;
NTSTATUS status;
devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
status = IoAcquireRemoveLock (&devExt->RemoveLock, Irp); if (!NT_SUCCESS (status)) {
// maybe device is being removed.
Irp->IoStatus.Information = ;
Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return status;
} // Do request-specific processing . . .
// Request-specific processing is done. Complete the IRP
// and release the lock. Irp->IoStatus.Status = status;
IoCompleteRequest (Irp, IO_NO_INCREMENT );
IoReleaseRemoveLock (&devExt->RemoveLock, Irp);
return status;
}
如果驱动程序将 IRP 传递给下一层驱动程序并设置一个 IoCompletion 例程,那么驱动程序将从 IoCompletion 例程调用 IoReleaseRemoveLock,如下所示:
NTSTATUS MyCompletionRoutine (
IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) {
PDEVICE_EXTENSION data;
UNREFERENCED_PARAMETER (DeviceObject);
data = (PDEVICE_EXTENSION) Context;
IoReleaseRemoveLock (&data->RemoveLock, Irp);
return STATUS_SUCCESS;
}
IO_REMOVE_LOCK小结
只有在对设备对象的所有引用都被释放后,I/O 管理器才会真正删除该设备对象。因此,在驱动程序的设备删除处理完成之后,有效的设备对象和设备扩展可能仍然存在。但是,驱动程序已经释放其资源,从而使得存储在设备扩展中的这些资源的指针都变得无效。
如果驱动程序在其删除设备处理完成之后,但是 I/O 管理器删除设备对象之前接收到另一个 I/O 请求,那么就会发生问题。当驱动程序处理请求时,它可能试图从设备扩展取消对一个无效指针的引用,这会导致系统崩溃。
要防止这种问题,驱动程序应该为所有类型的 I/O 请求获取删除锁,而不仅仅是即插即用和电源请求。大部分驱动程序已经在其 DispatchPnP 和 DispatchPower 例程中获取了删除锁,从而防止在处理即插即用和电源 IRP 时删除设备。但是,因为其他类型的 IRP 可能在设备删除之后到达,所以驱动程序还应该在调度例程中为其他类型的 I/O 请求获得删除锁。
最简单的方法是在发送 IRP 时在 I/O 调度例程中调用 IoAcquireRemoveLock。IoAcquireRemoveLock 返回 STATUS_DELETE_PENDING 来指示正在删除设备。如果 IoAcquireRemoveLock 返回此状态(或者除 STATUS_SUCCESS 之外的任何状态),那么驱动程序应该拒绝 I/O 请求。
在取消引用存储在设备扩展中的任何指针之前,通过在发送 IRP 时在 I/O 调度例程中调用 IoAcquireRemoveLock 来获得删除锁。 如果 IoAcquireRemoveLock 不返回 STATUS_SUCCESS,那么拒绝 I/O 请求。 当驱动程序完成 IRP 处理时,调用 IoReleaseRemoveLock。 在设备删除处理期间调用 IoReleaseRemoveLockAndWait,然后调用 IoDetachDevice 和 IoDeleteDevice。 Reference(其实基本都是转来的,稍微整理了一下) 我的设备不见了。为什么我仍然收到 IRP? 《Programming the Microsoft Windows Driver Model》
--------------------------------------------------------------------------------
原作者:代码疯子(Wins0n)
Copyed From 程序人生 Home Page:http://www.programlife.net Source URL:http://www.programlife.net/io_remove_lock.html
IO_REMOVE_LOCK使用方法小结(转载加改正)的更多相关文章
- ASP.NET对路径"C:/......."的访问被拒绝 解决方法小结 [转载]
问题: 异常详细信息: System.UnauthorizedAccessException: 对路径“C:/Supermarket/output.pdf”的访问被拒绝. 解决方法: 一.在IIS中的 ...
- python中执行shell命令的几个方法小结(转载)
转载:http://www.jb51.net/article/55327.htm python中执行shell命令的几个方法小结 投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014- ...
- 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理
服务器文档下载zip格式 刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...
- 【转】TextView长按复制实现方法小结
有这么一个需求,用户在浏览文本信息时希望长按信息就能弹出复制的选项方便保存或者在别的页面使用这些信息.类似的, 就像长按WebView或者EditText的内容就自动弹出复制选项. 这里面主要是2个特 ...
- static使用方法小结
static使用方法小结 statickeyword是C, C++中都存在的keyword, 它主要有三种使用方式, 当中前两种仅仅指在C语言中使用, 第三种在C++中使用(C,C++中详细细微操作不 ...
- T-SQL切割字符串方法小结
T-SQL切割字符串方法小结,只有表值函数那个是自己的思想,其它都是来源于网络的思想,请大家不要笑话,嘻嘻~网上大牛太多,这点东西虽然上不了台面,但是也算是自己的一个学习吧,能够对一个人有用也行.再不 ...
- PowerDesigner实用方法小结(1)
PowerDesigner使用方法小结 PowerDesigner多用来进行数据库模型设计,具有SQL语句自动生成等功能.当然,也有不少缺点,比如团队分享. 一.设置PowerDesigner模型视图 ...
- (转)java判断string变量是否是数字的六种方法小结
java判断string变量是否是数字的六种方法小结 (2012-10-17 17:00:17) 转载▼ 标签: it 分类: 转发 1.用JAVA自带的函数 public static boolea ...
- 梯度提升树(GBDT)原理小结(转载)
在集成学习值Adaboost算法原理和代码小结(转载)中,我们对Boosting家族的Adaboost算法做了总结,本文就对Boosting家族中另一个重要的算法梯度提升树(Gradient Boos ...
随机推荐
- POJ - 1741 Tree
DescriptionGive a tree with n vertices,each edge has a length(positive integer less than 1001).Defin ...
- SQL Server 2008 设计与实现笔记(一)
Chart5 create database MovieRental; select name, SUSER_SNAME(sid) as [login] from sys.database_princ ...
- java中线程池的使用方法
1 引入线程池的原因 由于线程的生命周期中包括创建.就绪.运行.阻塞.销毁阶段,当我们待处理的任务数目较小时,我们可以自己创建几个线程来处理相应的任务,但当有大量的任务时,由于创建.销毁线程需要很大的 ...
- Google 面经 09/26
http://www.mitbbs.com/article_t/JobHunting/32539885.html 狗家面经发信站: BBS 未名空间站 (Thu Sep 26 01:20:54 201 ...
- [译]C++书籍终极推荐
转载声明: 翻译仅以技术学习和交流为目的,如需转载请务必标明原帖链接. 来源:http://stackoverflow.com/questions/388242/the-definitive-c-bo ...
- redis的key过期时间
public void set(String key,String value,int liveTime){ this.set(key, value); this.getJedis().expire( ...
- CentOS7.1配置远程桌面
网上看了很多资料,完全是乱的. 我使用的是CentOS7.1的系统.我的要求是windows的客户机可以远程访问CentOS系统. 1,首先需要检查一下服务器是否已经安装了VNC服务,检查服务器的是否 ...
- 【流媒體】live555—VS2008 下live555编译、使用及测试
[流媒體]live555—VS22008 下live555编译.使用及测试 Ⅰ live555简介 Live555 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输协议如R ...
- EPEL库安装
EPEL是yum的一个软件源,里面包含了许多基本源里没有的软件了,但在我们在使用epel时是需要安装它才可以了.EPEL,即Extra Packages for Enterprise Linux的简称 ...
- Java API ——Arrays类
1.Arrays类概述 · 针对数组进行操作的工具类. · 提供了排序,查找等功能. 2.成员方法 · public static String toString(int[] a):in[] a可以改 ...