同步示例

以下示例说明了微型驱动程序在同步方面需要执行的作,并包括不应使用同步时的示例:

  • 示例一:具有正常运行的 ISR 的微型驱动程序

    如果启用了流类同步,所有微型驱动程序的入口点将使用 KeSynchronizeExecution 在提升的 IRQL 上被调用,这意味着在微型驱动程序执行代码时,适配器的 IRQ 级别及所有更低的 IRQ 级别都会被屏蔽。 因此,微型驱动程序必须在此模式下仅执行少量工作。

    微型驱动程序在提高 IRQL 时,不应运行通常需要超过 10 到 20 微秒的代码。 如果使用 stream.sys 的调试版本,流类会记录在提升的 IRQL 中花费的时间量,并在驱动程序花费过多时间时进行断言判断。 如果微型驱动程序只需为请求设置硬件 DMA 寄存器,或者只需在它的 ISR 中读取端口,那么通常可以接受在提高的 IRQL 下执行其所有处理。

    如果微型驱动程序需要执行需要多个微秒的处理,例如通过 PIO 传输数据的微型驱动程序,微型驱动程序应使用 StreamClassCallAtNewPriority 来计划DISPATCH_LEVEL回调。 在回调中,微型驱动程序最多可能需要 1/2 到 1 毫秒才能执行其处理。 在此模式下需要记住的一点是,DISPATCH_LEVEL回调 与 ISR 同步。

    如果微型驱动程序在回调期间以及 ISR 中访问资源(例如端口或队列)时硬件保持稳定,则这种缺乏同步并不是问题。 但是,如果不稳定可能是个问题,微型驱动程序必须使用 StreamClassCallAtNewPriority 来计划 HIGH 优先级回调,其中DISPATCH_LEVEL回调涉及与 ISR 使用的资源共享的资源。

    请注意,HIGH 优先级回调等效于调用 KeSynchronizeExecutionKeSynchronizeExecution 要求微型驱动程序引用 StreamClassCallAtNewPriority 不具有的多个参数,但通常这两个参数会导致相同的行为。

    如果微型驱动程序偶尔需要运行超过 1/2 到 1 毫秒的代码,或者偶尔需要在PASSIVE_LEVEL(如初始化时)调用服务,则可以使用 StreamClassCallAtNewPriority 设置为 LOW 优先级来获取PASSIVE_LEVEL工作线程。 请注意,低优先级回调未与任何内容同步,微型驱动程序可以在运行低优先级回调时接收新请求(假设 ReadyForNextRequest NotificationType 参数挂起)或 ISR 调用。

  • 示例二:没有 ISR 的微型驱动程序

    如果启用了流类同步功能,则所有微型驱动程序的入口点都会在 DISPATCH_LEVEL 调用。 微型驱动程序最多可以处理大约 1/2 到 1 毫秒的持续时间,而无需调整优先级。 如果微型驱动程序偶尔需要运行超过 1/2 毫秒的代码,或者偶尔需要在PASSIVE_LEVEL(如初始化时)调用服务,则可以使用具有 LOW 优先级的 StreamClassCallAtNewPriority 来获取PASSIVE_LEVEL工作线程。 请注意,低优先级回调与其他任何进程或事件都未同步,假设 ReadyForNextRequest NotificationType 参数处于挂起状态,微型驱动程序可能会在低优先级回调运行时接收到新请求。

  • 不应使用流类同步时

    下面是不应使用流类同步的情况的示例。 这些包括:

    • 当驱动程序频繁需要处理超过 1 毫秒的任务(例如超过 20% 的微型驱动程序接收的请求),或者频繁调用 PASSIVE_LEVEL 服务(如 Microsoft DirectDraw 服务)时。 使用 stream.sys的调试版本时,流类将断言这两种情况,并在启用同步时停止。

    • 当微型驱动程序是没有关联硬件的筛选器时。 此类微型驱动程序应在PASSIVE_LEVEL运行,因为没有要与之同步的基础硬件,微型驱动程序通常执行大量处理。 在这种情况下,自己进行同步操作比使用流类同步更高效,因为后者会带来额外的开销。 如有必要,请使用互斥体来保护队列。

      同步代码中的缺陷通常很难发现,在某些环境中(例如在多处理器系统上运行的基于 NT 的操作系统中),缺陷可能仅在经过数小时的高负载压力测试后才会显现。 根据供应商的经验,这类事情并不是大多数供应商有能力或愿意去调试的。 只有熟悉编写完全异步 WDM 设备驱动程序的驱动程序编写器才应尝试执行自己的同步。

    • 当微型驱动程序是总线对总线类型的驱动程序(例如,USB 或 1394 外围设备的驱动程序)时,它不需要真正担心实际硬件的同步问题,而只是需要在 PASSIVE_LEVEL 调用请求到下一层,并通常在 DISPATCH_LEVEL 接收回调。