WDF 驱动程序何时可以使用电源管理的 I/O 队列
Updated: April 23, 2008
对于 Windows Driver Foundation (WDF),驱动程序的读取、写入和设备 I/O 控制请求必须由 WDF 队列对象处理。该队列对象从系统接收请求并将它们分发给驱动程序的回调函数。其他类型的请求(例如即插即用和电源管理请求)可以由回调函数直接处理。
队列对象可以具有电源管理特性或不具有电源管理特性。当设备离开其工作状态时 (D0):
| • | 电源管理队列对象继续对请求进行排队,但是会在将请求分发给驱动程序之前一直等待,直到设备重新进入 D0。 |
| • | 非电源管理队列对象继续分发请求(正如其在设备进入 D0 时的操作一样)。 |
电源管理队列的关键问题是处理空闲设备的请求,在这种情况下,系统仍然处于工作状态 (S0),但是设备功率已经被降低到低电源状态 (Dx)。电源管理队列处理传入的空闲设备请求的方式取决于驱动程序是否是堆栈的电源策略所有者 (PPO)。
| • | PPO PPO 管理设备堆栈的电源策略。对于典型的设备堆栈,功能驱动程序充当 PPO。但是,对于基于原始物理设备对象 (PDO) 的堆栈,总线驱动程序充当 PPO。PPO 的电源管理队列支持空闲设备的上电 (power-up) 逻辑。如果当 I/O 请求到达时设备是空闲的,那么队列的上电逻辑初始化将设备返回到 D0 的过程,这样,队列就可以分发请求。
默认情况下,PPO 的队列对象具有电源管理特性。 |
| • | 非 PPO 筛选器驱动程序很少充当 PPO,总线驱动程序也很少充当 PPO(除非堆栈基于原始 PDO)。非 PPO 的电源管理队列不支持上电逻辑。如果当 I/O 请求到达时设备处于空闲状态,那么队列对象对请求排队,但是不初始化唤醒过程。
默认情况下,非 PPO 队列对象没有电源管理特性。要使用电源管理队列,非 PPO 必须在创建队列对象时显式启用电源管理。 |
您在创建队列时将一个队列配置成电源管理,对于内核模式驱动程序框架 (KMDF) 驱动程序,通常在 EvtDriverDeviceAdd 回调例程中完成,对于用户模式驱动程序框架 (UMDF) 驱动程序,通常在 IDriverEntry::OnDeviceAdd 回调例程中完成。
下面的例子显示如何为 KMDF 筛选器驱动程序创建一个手动的电源管理队列。
NTSTATUS MyDriver_EvtDriverDeviceAdd( __in WDFDRIVER Driver, __in PWDFDEVICE_INIT DeviceInit ) { WDFDEVICE device; WDF_IO_QUEUE_CONFIG ioQueueConfig; PDEVICE_CONTEXT pDevContext; WDFQUEUE queue;
...
WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig, WdfIoQueueDispatchManual);
ioQueueConfig.PowerManaged = WdfTrue;
status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, &pDevContext->InterruptMsgQueue ); ...}调用 IWDFDevice::CreateIoQueue 来创建队列对象时,UMDF 驱动程序通过将 bPowerManaged 设置为 TRUE 来将队列配置为电源管理。
进行正确配置之后,电源管理队列可以极大地简化驱动程序实现,但是它们并不适用于所有驱动程序。每个设备堆栈有一个指定为 PPO 的驱动程序。这个驱动程序管理设备的电源策略并控制设备的电源状态。堆栈中的任何驱动程序都可以是 PPO,但是通常由功能驱动程序处理这个任务。如果堆栈中 PPO 上的驱动程序使用一个电源管理队列,那么该驱动程序可能创建一个死锁并使堆栈停止。
当设备空闲和功率下降时会出现问题。在那种情况下,整个设备堆栈的功率都被降低,但是系统仍然处于 S0 中并且可以发送 I/O 请求到设备。当系统发送 I/O 请求时,设备堆栈应该唤醒设备,从而使其可以处理请求。如果 PPO 上的驱动程序实现一个电源管理队列,那么这种期望的行为不会发生。例如,一个由上层筛选器 (Driver A) 和功能驱动程序 (Driver B) 组成的简单堆栈。(Driver B) 是堆栈的 PPO。
考虑这个堆栈的两个电源管理场景:
| • | 场景 1:Driver A 使用非电源管理的队列来处理所有 I/O 请求。 |
| • | 场景 2:Driver A 使用一个电源管理队列来处理所有读/写请求,使用一个非电源管理的默认队列来处理所有其他 I/O 请求。 |
对于这两种场景,Driver B 使用电源管理队列来处理所有请求。
图 1 显示场景 1 中如何处理 I/O 请求。

图 1:场景 1
所有读取、写入和设备 I/O 控制请求都以相同的方式进行处理,如下所示:
1. | 设备最初被降低功率并处于 Dx 状态。 |
2. | 系统发送一个 I/O 请求到设备。 |
3. | Driver A 的 I/O 队列对象接收到请求。 |
4. | 该队列对象将请求分发给 Driver A。 |
5. | Driver A 处理请求,然后将请求发送到它的 I/O 目标 (Driver B)。 |
6. | Driver B 的队列对象接收到请求并初始化唤醒过程(这里未显示详细过程)。 |
7. | 设备唤醒之后,Driver B 的队列对象将请求分发给Driver B(它将请求发送到现在已经唤醒的设备)。 |
场景 1 演示了驱动程序使用电源管理队列的正确方式。请求被迅速处理,用户不应该察觉任何延迟(可能除了唤醒设备需要的时间)。
图 2 显示在场景 2 中如何处理读取请求,以及设备 I/O 控制请求。

图 2:场景 2
在这种情况下,读取请求的处理与设备 I/O 控制请求的处理有所不同,如下所示:
1. | 设备最初被降低功率,处于 Dx 状态。 |
2. | 系统向设备发出一个读请求。 |
3. | Driver A 的电源管理的读/写队列对象接收到请求。因为 Driver A 不是 PPO,所以队列对象不能初始化唤醒过程。相反,队列对象挂起请求,直到它被通知设备已经重新进入 D0(这可能会消耗一些时间)。 |
4. | 经过一些时间之后,系统发送一个设备 I/O 控制请求(由 Driver A 的默认队列接收)。 |
5. | 因为默认队列不支持电源管理,所以它将请求分发给 Driver A,Driver A 处理请求并将其发送给 Driver B。 |
6. | 驱动程序 B 的电源管理队列对象初始化唤醒过程,然后将请求发送到已经唤醒的设备。 |
7. | Driver A 的读/写队列对象接收到一个上电事件并将读取请求分发给 Driver A,Driver A 处理请求并将其发送到Driver B。 |
可以从步骤 3 中看出 Driver A 不应该使用电源管理队列的原因。堆栈在该处发生死锁并保持该状态,直到不由电源管理队列处理的请求到达(在这个例子中是设备 I/O 控制请求)。根据驱动程序的实现方式和传入请求的类型和频率,Driver A 的电源管理读/写队列可以让堆栈停止一段时间(可能无限期停止)。
电源管理队列和 UMDF 驱动程序
UMDF 驱动程序可以通过调用 IWDFDeviceInitialize::SetPowerPolicyOwnership 显式地将其本身指定为 PPO,但是它们通常不会这样做。UMDF 驱动程序的 PPO 通常是一种下层内核模式驱动程序。如果这样的话,所有的 UMDF 驱动程序都在 PPO 上方,并且可以使堆栈停止(只要它们使用电源管理队列)。
UMDF USB 驱动程序是可以通过使用电源管理队列来停止堆栈的驱动程序的出色示例。所有的 UMDF USB 驱动程序都依赖于下层内核模式驱动程序 WinUSB.sys,它是默认的 PPO。UMDF USB 驱动程序不应该试图覆盖这个名称并声称电源策略所有权。
UMDF USB 驱动程序可以使用电源管理队列,但是其适用性取决于驱动程序支持 USB 选择性暂停还是只响应系统唤醒事件。
| • | 支持选择性暂停的 UMDF 驱动程序不应该使用电源管理队列。对于选择性暂停,传入的 I/O 请求应该唤醒设备。但是,请求必须首先被分发到 UMDF 驱动程序,然后才能被转发给 WinUSB.sys,WinUSB.sys 然后可以初始化唤醒过程。如前一节所述,电源管理队列直到设备重新进入 D0 后才分发传入的请求。如果 UMDF 驱动程序使用一个电源管理队列,那么队列在设备重新进入 D0 之前不分发任何请求,从而使堆栈停止。 |
| • | 只响应系统唤醒事件的 UMDF 驱动程序可以使用电源管理队列。在这种情况下,重新进入 D0 的游戏只响应系统电源转换。这些事件由即插即用和电源回调程(不是由 I/O 队列)进行处理,因此它们可以到达 WinUSB.sys 并在合适的时候唤醒设备(不管 UMDF 驱动程序的 I/O 请求是否支持电源管理)。 |
您应该做什么?
| • | 位于 PPO 上面并且必须唤醒空闲设备的 WDF 驱动程序应该只使用非电源管理 I/O 队列。 |
| • | 支持选择性暂停的 UMDF USB 驱动程序应该只使用非电源管理 I/O 队列。 |
| • | UMDF USB 驱动程序不应该声称电源策略所有权。 |
更多信息:
MSDN 上 Peter Wieland 的 Blog
如何在 USB 设备的 UMDF 驱动程序中启用 USB 选择性暂停和系统唤醒
I/O 队列的电源管理 (KMDF)
http://msdn2.microsoft.com/en-us/library/aa490123.aspx
USB 驱动程序中的选择性暂停
http://www.microsoft.com/whdc/driver/wdf/USB_select-susp.mspx