使用计时器对象

下图演示了如何使用通知计时器为操作设置超时间隔,然后在其他驱动程序例程处理 I/O 请求时等待。

说明正在等待计时器对象的示意图。

如上图所示,驱动程序必须为计时器对象提供存储,该对象必须通过调用 KeInitializeTimer 并具有指向此存储的指针来初始化该对象。 驱动程序通常从其 AddDevice 例程进行此调用。

在特定线程(例如驱动程序创建的线程或请求同步 I/O 操作的线程)的上下文中,驱动程序可以等待其计时器对象,如上图所示:

  1. 线程使用指向计时器对象的指针和给定的 DueTime 值(以 100 纳秒为单位)调用 KeSetTimerDueTime 的正值指定计时器对象应从内核的计时器队列中删除并设置为“已信号”状态的绝对时间。 DueTime 的负值指定相对于当前系统时间的间隔。

    请注意,在系统线程中运行的线程 (或驱动程序例程) 传递 DPC 对象的 NULL 指针, (如图所示,在调用 KeSetTimer 对象而不是排队 CustomTimerDpc 例) 程时,在调用 KeSetTimer 对象而不是排队 CustomTimerDpc 例程时,为 CustomTimerDpc 例程使用计时器和 DPC 对象。

  2. 线程使用指向计时器对象的指针调用 KeWaitForSingleObject ,这会在计时器对象位于内核的计时器队列中时将线程置于等待状态。

  3. 给定 的 DueTime 过期。

  4. 内核取消对计时器对象的排队,将其设置为“已信号”状态,并将线程的状态从“等待”更改为“就绪”。

  5. 当处理器可用时,内核会立即调度线程以执行:也就是说,当前没有其他具有更高优先级的线程处于就绪状态,并且没有内核模式例程可在更高的 IRQL 上运行。

在 IRQL >= DISPATCH_LEVEL 运行的驱动程序例程可以通过将计时器对象与关联的 DPC 对象结合使用来将驱动程序提供的 CustomTimerDpc 例程排队来超时请求。 只有非比特线程上下文中运行的驱动程序例程才能等待计时器对象的非零间隔,如上图所示。

与所有其他线程一样,驱动程序创建的线程由内核线程对象表示,该对象也是调度程序对象。 因此,驱动程序不需要让其驱动程序创建的线程使用计时器对象来自愿将自身置于给定间隔的等待状态。 相反,线程可以使用调用方提供的间隔调用 KeDelayExecutionThread 。 有关此方法的详细信息,请参阅 轮询设备

DriverEntryReinitializeUnload 例程也在系统线程上下文中运行,因此驱动程序可以在初始化或卸载时使用驱动程序初始化的计时器对象或 KeDelayExecutionThread 调用 KeWaitForSingleObject。 如果设备驱动程序必须在初始化期间等待设备更新状态,则设备驱动程序可以调用 KeStallExecutionProcessor , (最好小于 50 微秒) 。

但是,更高级别的驱动程序通常在其 DriverEntry重新初始化 例程中使用另一种同步机制,而不是使用计时器对象。 应始终将更高级别的驱动程序设计为在特定类型设备的任何较低级别驱动程序上分层。 因此,如果更高级别的驱动程序等待计时器对象或调用 KeDelayExecutionThread ,则加载速度往往会变慢,因为此类驱动程序必须等待足够长的时间间隔才能容纳支持它的最慢设备。 另请注意,很难确定此类等待的“安全”但最小间隔。

同样,PnP 驱动程序不应等待其他操作发生,而应使用 PnP 管理器的 通知 机制。