EvtCleanupCallback 什么时候运行?

Updated: June 12, 2006

Kernel-Mode Driver Framework (KMDF) 对象是按层次结构组织的,KMDF 按照层次顺序删除对象。从离父对象最远的对象开始删除对象,沿着对象层次结构向上往根移动。KMDF 采用下列步骤来删除对象:

1.

从离父对象最远的子对象开始,调用对象的 EvtCleanupCallback。在这个例程中,驱动程序应该执行任何必须在删除对象的父对象之前进行的清除任务。这些任务可能包含释放对象或父对象上的显式引用。注意,当 EvtCleanupCallback 例程运行时,对象的子对象仍然存在(即使它们的 EvtCleanupCallback 例程已经被调用)。

2.

当对象的引用计数达到零时,调用对象的 EvtDestroyCallback(如果驱动程序已经注册了一个回调例程)。

3.

取消分配被分配给对象及其上下文区域的内存。

调用 EvtCleanupCallback 的顺序
KMDF 在调用父对象的 EvtCleanupCallback 例程之前调用子对象的该例程,从而保证在子对象的 EvtCleanupCallback 例程运行时父对象仍然存在,但有一个例外。

这种保证顺序的例外情况适用于驱动程序在 DISPATCH_LEVEL 级别上完成的 I/O 请求。如果这种 I/O 请求对象有一个或多个其 EvtCleanupCallback 例程必须在 PASSIVE_LEVEL 级别上调用的子对象,那么可以在删除一个或多个子对象之前删除父对象请求。如果对象必须等待某些操作完成或访问分页的内存,那么它需要在 PASSIVE_LEVEL 级别上进行清除。

KMDF 在 PASSIVE_LEVEL 调用下列类型对象的 EvtCleanupCallback 例程:

WDFDEVICE

WDFTIMER

WDFDPC

WDFWORKITEM

包含分页内存池的 WDFMEMORY 对象

分配分页内存池的 WDFLOOKASIDE 对象

WDFQUEUE

WDFIOTARGET

WDFSTRING

如果驱动程序试图在这样一个对象(或这种对象的父对象)以 DISPATCH_LEVEL 级别运行时删除它,那么 KMDF 将 EvtCleanupCallback 排队到一个工作项以便以后在 PASSIVE_LEVEL 级别进行处理,然后调用父对象的清除回调例程(无需确定子对象的回调例程是否已经运行)。当前,KMDF 为每个 WDFDEVICE 对象维护一个工作项队列,在这个工作项队列上为设备对象及其子对象调用 EvtCleanupCallback 例程。

考虑当驱动程序完成一个具有子计时器对象的 I/O 请求时会发生什么。根据 WDFREQUEST 对象的“约定”,KMDF 在底层 Microsoft Windows Driver Model (WDM) I/O 请求包 (IRP) 的指针仍然有效并且可以访问的时候调用请求对象的 EvtCleanupCallback。出于性能原因,KMDF 必须尽快完成 IRP。但是,一旦 IRP 完成,指针就不再有效。KMDF 将计时器的清除回调例程排队到一个工作项中然后完成 IRP,而不是在任何 EvtCleanupCallback 例程已经以 PASSIVE_LEVEL 级别运行之后等待完成 IRP。这样,计时器的 EvtCleanupCallback 可以在请求被完成之后、IRP 指针被释放之后和请求对象被删除之后运行。

要避免这种行为可能导致的任何问题,驱动程序不应该将 WDFREQUEST 对象设置为任何需要在 PASSIVE_LEVEL 级别上清除的对象的父对象。默认情况下,大多数对象的父对象是 WDFDEVICE,因此驱动程序应该只接受默认值。一般来讲,如果 WDFDEVICE 对象被作为参数传递(直接传递或作为结构的一部分传递)给创建对象的方法,那么 WDFDEVICE 是默认的父对象。关于默认父对象的完整列表,请参见 Windows Driver Kit (WDK) 文档。

驱动程序可以通过设置 ParentObject 属性来更改大多数 KMDF 对象的父对象。通过合适地设置父对象/子对象的关系,驱动程序可以避免去掉对相关对象的显式引用,并且可以使用层次结构和关联的回调例程来管理对象的生存期。

此外,请注意,需要在 PASSIVE_LEVEL 级别上进行清除的对象并不一定要在 PASSIVE_LEVEL 级别上分配。WDFWORKITEM、WDFTIMER、WDFDPC 和 WDFQUEUE 对象可以在 DISPATCH_LEVEL 级别上创建,而 WDFMEMORY 和 WDFLOOKASIDE 对象使用未分页的内存池。不能在 DISPATCH_LEVEL 级别上创建使用分页内存池的 WDFMEMORY 和 WDFLOOKASIDE 对象以及 WDFDEVICE、WDFIOTARGET 和 WDFSTRING 对象。

对 EvtDestroyCallback 的调用顺序
KMDF 在调用对象的 EvtCleanupCallback 例程之后和对象的引用计数达到零之后调用其 EvtDestroyCallback 例程。对象及其子对象的 EvtDestroyCallback 例程可以以任何顺序调用,从而父对象的 EvtDestroyCallback 例程可以在其任一子对象的该例程被调用之前被调用。EvtDestroyCallback 可以访问对象上下文,但是不能调用对象的任何方法。

您应该做什么?

不要将 WDFREQUEST 对象设置为任何需要在 PASSIVE_LEVEL 级别上清除的对象的父对象。

不要试图从对象的 EvtDestroyCallback 调用其方法。

更多信息:
内核模式驱动程序框架的体系结构
Windows Driver Kit (WDK):“框架对象汇总”



此信息有用吗?