参考文档:Understanding When Remove IRPs Are Issued - Windows drivers
Windows对于PnP(即插即用)设备,有一个专门的IRP Major Code定义:IRP_MJ_PNP,任何PnP设备驱动以及工作在PnP设备协议栈之上的驱动(如文件系统驱动)和过滤驱动都需要处理IRP_MJ_PNP请求,尤其是删除的过程,如果驱动没有针对这类请求进行处理,可能会导致系统挂起或者删除设备失败。一个常见的用户层面表现就是无法安全弹出U盘。
本文主要翻译自前面的链接,不保证内容一定正确(未进行完整验证)。
PnP设备有两种删除流程,一种是正常删除,也就是用户层面发起的“安全弹出”请求;另一种是用户直接拔出设备,这种是非正常删除。
对于正常安全删除的流程:
Windows会先发起一个IRP_MN_QUERY_REMOVE_DEVICE请求,去查询设备是否可以正常删除,如果此时设备忙,则可以通过Irp->IoStatus.Status返回一个STATUS_UNSUCCESSFUL,同时调用IoCompleteRequest,并且不传播Irp给下层驱动。这样在用户层面上,就会提示设备忙,无法删除。
如果驱动认为设备时可以删除的,那么收到此请求以后,就应该禁止新的其它请求进入,同时设置Irp->IoStatus.Status为STATUS_SUCCESS,并调用IoSkipCurrentIrpStackLocation和IoCallDriver传播这个Irp到更底层的设备。
通常来说,如果这个请求返回成功,那么驱动此时就应该释放一些资源,当然驱动也可以选择不释放,而是仅仅阻止更多的请求进入,具体驱动选择哪种行为,由驱动自己决定,但通常情况下,对设备加锁是必需的,也要阻止任何“创建”类型的请求进入驱动。
当IRP_MN_QUERY_REMOVE_DEVICE返回成功,并且整个设备栈上的所有驱动都返回成功时,Windows的PnP管理器会发送IRP_MN_REMOVE_DEVICE请求,完成对设备彻底删除,当收到IRP_MN_REMOVE_DEVICE请求时,驱动需要释放全部资源,关闭设备,停止还在执行中的其它Irp请求。同时,驱动也要传播这个请求到下层驱动。
Windows文档中要求:IRP_MN_REMOVE_DEVICE请求是必须成功的,不能失败,否则可能会造成设备状态信息不一致。
IRP_MN_REMOVE_DEVICE请求有可能是驱动被卸载前的最后一个请求,所以必须要保证所有资源被正确释放。
如果IRP_MN_QUERY_REMOVE_DEVICE返回失败,那么Windows会重新发送一个IRP_MN_CANCEL_REMOVE_DEVICE请求通知驱动以及整个设备栈取消之前的请求。
当驱动收到IRP_MN_CANCEL_REMOVE_DEVICE时,需要解开设备锁定的状态,并允许“创建”类型的请求进入驱动。类似地,IRP_MN_CANCEL_REMOVE_DEVICE也要被传播到底层驱动,通知底层设备驱动更新状态。
还有一种特例是,虽然IRP_MN_QUERY_REMOVE_DEVICE返回成功,但上层代码改变主意,不希望删除设备,那么也会发送一个IRP_MN_CANCEL_REMOVE_DEVICE请求,对于这个请求的处理跟前面描述的是一致的:更新状态,传播Irp到底层。
所以大致的流程图如下
IRP_MN_QUERY_REMOVE_DEVICE(查询是否可以删除)
|
+-----(失败)---> IRP_MN_CANCEL_REMOVE_DEVICE
|
+-----(用户层面取消请求)---> IRP_MN_CANCEL_REMOVE_DEVICE
|
(成功)
v
IRP_MN_REMOVE_DEVICE (可能是最后一个IRP)
对于非正常拔出设备的删除流程:
Windows PnP管理器会发送一个IRP_MN_SURPRISE_REMOVAL请求给驱动,通知驱动这个设备已经被强制删除了。
这个时候驱动需要做的事情如下:
1. 检查设备是否还存在,如果还存在,需要停止并删除设备(笔者注:这段状态很奇怪,但文档里是这么写的)
2. 释放全部资源,阻止新的IO请求。
3. 除了IRP_MJ_CLEANUP,IRP_MJ_CLOSE,IRP_MJ_POWER,IRP_MJ_PNP之外,其它IRP都需要被阻止。
4. 如果过滤驱动不能处理这个请求,向底层传播。
5. 设置Irp->IoStatus.Status为STATUS_SUCCESS,必须成功,不能失败。
6. 调用IoSkipCurrentIrpStackLocation和IoCallDriver传播IRP
7. 通过IoCallDriver获得返回值并返回IRP dispatch函数
8. 不要调用任何Complete函数
当IRP_MN_SURPRISE_REMOVAL请求返回以后,Windows会继续发送一个IRP_MN_REMOVE_DEVICE完成设备删除,流程与正常安全移除设备的方式一致。
BTW:我一个VxWorks码农,为啥要写Windows驱动呢?