使用 CustomTimerDpc 例程

若要禁用以前设置的计时器对象,驱动程序会调用 KeCancelTimer。 此例程从系统的计时器队列中删除计时器对象。 通常,计时器对象不会设置为信号状态, 并且 CustomTimerDpc 例程不会排队等待执行。 但是,如果在调用 KeCancelTimer 时计时器即将过期,则可能会在 KeCancelTimer 有机会访问时间队列之前过期,在这种情况下,将发生信号和 DPC 排队。

在之前指定的时间间隔过期之前,使用先前指定的 TimerDpc 指针召回 KeSetTimer 或 KeSetTimerEx 将产生以下效果:

  • 内核从计时器队列中删除计时器对象,而无需将对象设置为信号状态或排队 CustomTimerDpc 例程。

  • 内核使用新的 DueTime 值重新插入计时器队列中的计时器对象。

将同一计时器对象用于不同的目的可能会导致争用条件或严重的驱动程序错误。 例如,假设驱动程序指定单个计时器对象来设置对 CustomTimerDpc 例程的调用,并在驱动程序专用线程中设置等待。 每当驱动程序专用线程为通用计时器对象调用 KeSetTimerKeSetTimerExKeCancelTimer 时,如果计时器对象已排队等待 CustomTimerDpc 调用,则线程将取消对 CustomTimerDpc 例程的调用。

如果驱动程序具有 CustomTimerDpc 例程,并且还在非比特线程上下文中等待计时器对象,则它应:

  • 切勿在非比特线程上下文中使用线程上下文敏感计时器对象,反之亦然。

  • 为每个 CustomTimerDpc 例程分配单独的计时器对象。 在非比特线程上下文中调用的每组驱动程序线程或驱动程序例程都应有自己的一组“可等待”计时器对象。

如果使用 CustomTimerDpc 例程,请仔细选择驱动程序在调用 KeSetTimerKeSetTimerEx 时传递的间隔。 此外,请考虑从发出此调用的任何驱动程序例程(特别是在 SMP 平台上)使用相同的计时器对象调用 KeCancelTimer 的所有可能的影响。

请记住以下有关 CustomTimerDpc 例程的事实:

在任何给定时刻,只能对表示特定 DPC 例程的 DPC 对象的一次实例化排队等待执行。

如果第二个驱动程序例程在第一个调用 指定的时间间隔过期之前调用 KeSetTimer 或 KeSetTimerEx 来运行相同的 CustomTimerDpc 例程,则 CustomTimerDpc 例程仅在第二个调用方指定的间隔到期后运行。 在这些情况下, CustomTimerDpc 不执行第一个例程称为 KeSetTimerKeSetTimerEx 的工作。

对于具有 CustomTimerDpc 例程并使用定期计时器的驱动程序:

驱动程序无法从 DPC 例程解除分配定期计时器。 驱动程序只能从 DPC 例程解除分配非定期计时器。

对于同时具有 CustomDpc 和 CustomTimerDpc 例程的驱动程序,请考虑以下设计准则:

若要防止争用条件,切勿将同一 Dpc 指针传递给 KeSetTimerKeSetTimerExKeInsertQueueDpc

换句话说,假设驱动程序的 StartIo 例程调用 KeSetTimerKeSetTimerExCustomTimerDpc 例程排队,而驱动程序的 ISR 同时从具有相同 Dpc 指针的另一个处理器调用 KeInsertQueueDpc。 当处理器上的 IRQL 低于DISPATCH_LEVEL或计时器间隔过期(以先到者为准)时,将运行该 DPC 例程。 无论哪一个是第一位的,DPC 例程将直接删除 StartIo 或 ISR 的一些基本工作。

此外,与单独的 CustomTimerDpcCustomDpc 例程相比,两个功能截然不同的标准驱动程序例程使用的 DPC 的性能特征会更差。 DPC 必须确定要执行哪些操作,具体取决于导致 StartIo 例程或 ISR 将其排队的条件。 在 DPC 中测试这些条件将使用额外的 CPU 周期。