使用 IoTimer 例程
启用关联设备对象的计时器时, IoTimer 例程大约每秒调用一次。 但是,由于调用每个 IoTimer 例程的时间间隔取决于系统时钟的分辨率,因此不要假设将在一秒的边界上精确调用 IoTimer 例程。
注意 与所有 DPC 例程一样, IoTimer 例程在 IRQL = DISPATCH_LEVEL调用。 当 DPC 例程运行时,阻止所有线程在同一处理器上运行。 驱动程序开发人员应仔细设计其 IoTimer 例程,使其尽可能短地运行一段时间。
IoTimer 例程最常见的用途可能是使 IRP 的设备 I/O 操作超时。 请考虑以下方案,将 IoTimer 例程用作设备驱动程序中的运行计时器:
启动设备时,驱动程序会将设备扩展中的计时器计数器初始化为 -1,指示没有当前设备 I/O 操作,并在返回STATUS_SUCCESS之前调用 IoStartTimer 。
每次调用 IoTimer 例程时,它都会检查计时器计数器是否为 -1,如果是,则返回控制权。
驱动程序的 StartIo 例程将设备扩展中的计时器计数器初始化为上限,并在 刚刚运行 IoTimer 例程的情况下再初始化一秒。 然后,它使用 KeSynchronizeExecution 调用 SynchCritSection_1 例程,该例程针对当前 IRP 请求的操作对物理设备进行编程。
驱动程序的 ISR 在排队驱动程序的 DpcForIsr 例程或 CustomDpc 例程之前,将计时器计数器重置为 -1。
每次调用 IoTimer 例程时,它都会检查 ISR 是否已将计时器计数器重置为 -1,如果是,则返回控制权。 否则, IoTimer 例程使用 KeSynchronizeExecution 调用 SynchCritSection_2 例程,该例程按驱动程序确定的秒数调整计时器计数器。
只要当前请求尚未超时, SynchCritSection_2 例程会将 TRUE 返回到 IoTimer 例程。如果计时器计数器为零, 则SynchCritSection_2 例程会将计时器计数器重置为驱动程序确定的重置超时值,为自身设置 (和 DpcForIsr) 在其上下文区域中设置重置预期标志,尝试重置设备,并返回 TRUE。
如果 SynchCritSection_2 例程的重置操作在设备上也超时,当它返回 FALSE 时,将再次调用该例程。 如果重置成功, DpcForIsr 例程将确定设备已从重置预期标志重置,并重试请求,并重复 StartIo 例程的操作,如步骤 2 中所述。
如果 SynchCritSection_2 例程返回 FALSE, IoTimer 例程假定物理设备处于未知状态,因为尝试重置它已失败。 在这些情况下, IoTimer 例程会将 CustomDpc 例程排队并返回 。 此 CustomDpc 例程记录设备 I/O 错误、调用 IoStartNextPacket、失败当前 IRP 并返回。
如果此设备驱动程序的 ISR 将共享计时器计数器重置为 -1(如步骤 3 中所述),则驱动程序的 DpcForIsr 例程将完成当前 IRP 的中断驱动 I/O 处理。 重置计时器计数器指示此设备 I/O 操作未超时,因此 IoTimer 例程不需要更改计时器计数器。
在大多数情况下,前面的 SynchCritSection_2 例程只是递减计时器计数器。 仅当当前 I/O 操作超时(当计时器计数器变为零时指示)时, SynchCritSection_2 例程才会尝试重置设备。 仅当尝试重置设备已失败时,SynchCritSection_2例程才会向 IoTimer 例程返回 FALSE。
因此,在正常情况下,前面的 IoTimer 例程及其帮助 程序SynchCritSection_2 例程执行所需的时间非常少。 通过以这种方式使用 IoTimer 例程,设备驱动程序可确保可以在必要时重试每个有效的设备 I/O 请求,并且仅当无法更正的硬件故障阻止满足 IRP 时, CustomDpc 例程才会使 IRP 失败。 此外,驱动程序以很少的执行时间成本提供此功能。
上述方案的简单性取决于一次只执行一个操作的设备,以及通常不与 I/O 操作重叠的驱动程序。 执行重叠设备 I/O 操作的驱动程序,或使用 IoTimer 例程超时发送到多个较低驱动程序链的一组驱动程序分配的 IRP 的更高级别的驱动程序,将具有更复杂的超时方案需要管理。