使用自动同步

基于框架的驱动程序中的几乎所有代码都驻留在事件回调函数中。 框架自动同步驱动程序的大部分回调函数,如下所示:

该框架使用一组内部同步锁来实现此自动同步。 框架确保两个或多个线程不能同时调用相同的回调函数。 每个线程必须等待,直到它可以在调用回调函数之前获取同步锁。 ((可选)驱动程序还可以在必要时获取这些同步锁。有关详细信息,请参阅 Using Framework Locks。)

驱动程序应在 对象上下文空间中存储特定于对象的数据。 如果驱动程序仅使用框架定义的接口,则只有接收对象句柄的回调函数才能访问此数据。 如果框架正在同步对驱动程序回调函数的调用,则一次只调用一个回调函数。 对象的上下文空间一次只能访问一个回调函数。

除非驱动程序实现 被动级中断处理,否则服务中断和访问中断数据的代码必须在设备的 IRQL (DIRQL) 上运行,并且需要额外的同步。 有关详细信息,请参阅 同步中断代码

如果驱动程序启用处理 I/O 请求的回调函数的自动同步,框架会同步这些回调函数,以便它们一次运行一个。 下表列出了框架同步的回调函数。

对象类型 同步回调函数

Queue 对象

请求处理程序EvtIoQueueStateEvtIoResumeEvtIoStop

文件对象

所有 回调函数

请求对象

EvtRequestCancel

(可选)框架还可以将这些回调函数与驱动程序为设备提供的任何中断、DPC、工作项和计时器对象回调函数同步(不包括中断对象的 EvtInterruptIsr 回调函数)。 若要启用此附加同步,驱动程序必须将这些对象的配置结构的 AutomaticSerialization 成员设置为 TRUE

总之,框架的自动同步功能提供以下功能:

  • 该框架始终同步每个设备的 PnP 和电源管理回调函数。

  • (可选)框架可以同步 I/O 队列的请求处理程序和其他一些回调函数(请参阅上表)。

  • 驱动程序可以请求框架同步中断、DPC、工作项和定时器对象的回调函数。

  • 驱动程序必须使用 同步中断代码中所述的技术来同步服务中断和访问中断数据的代码。

  • 该框架不会同步驱动程序的其他回调函数,例如驱动程序的 CompletionRoutine 回调函数或 I/O 目标对象定义的回调函数。 相反,框架提供了其他,这些锁可以让驱动程序用于同步这些回调函数。

选择同步范围

可以选择让框架同步与设备的所有 I/O 队列关联的所有回调函数。 或者,可以选择让框架单独同步每个设备的 I/O 队列的回调函数。 驱动程序可用的同步选项如下所示:

  • 设备级同步

    框架同步所有设备的 I/O 队列的回调函数,使它们依次独立运行。 框架通过在调用回调函数之前获取设备的同步锁来实现此同步。

  • 队列级同步

    框架会同步每个 I/O 队列的回调函数,以便它们可以依次运行。 该框架通过在调用回调函数之前获取队列的同步锁来实现此同步。

  • 无同步

    该框架不会同步上表中回调函数的执行,并且不会在调用回调函数之前获取同步锁。 如果需要同步,驱动程序必须提供它。

若要指定框架是希望框架为驱动程序提供设备级同步、队列级同步还是没有同步,请为驱动程序对象、设备对象或队列对象指定 同步范围 。 对象的WDF_OBJECT_ATTRIBUTES结构的 SynchronizationScope 成员标识对象的同步范围。 驱动程序可以指定的同步范围值为:

WdfSynchronizationScopeDevice
框架通过获取设备对象的同步锁来同步。

WdfSynchronizationScopeQueue
框架通过获取队列对象的同步锁来同步。

WdfSynchronizationScopeNone
框架不会同步,也不会获取同步锁。

WdfSynchronizationScopeInheritFromParent
框架从对象的父对象获取对象的 SynchronizationScope 值。

一般情况下,请避免使用设备级同步。

有关同步范围值的详细信息,请参阅 WDF_SYNCHRONIZATION_SCOPE

驱动程序对象的默认同步范围是 WdfSynchronizationScopeNone。 设备和队列对象的默认同步范围是 WdfSynchronizationScopeInheritFromParent

若要使用框架为所有设备提供设备级同步,请将 SynchronizationScope 设置为驱动程序对象的WDF_OBJECT_ATTRIBUTES结构中的 WdfSynchronizationScopeDevice。 对每个设备对象使用默认 WdfSynchronizationScopeInheritFromParent 值。

若要为单个设备提供设备级同步,请使用驱动程序对象的默认 WdfSynchronizationScopeNone 值。 在单个设备对象的WDF_OBJECT_ATTRIBUTES结构中,将 SynchronizationScope 设置为 WdfSynchronizationScopeDevice

如果希望框架为设备提供队列级同步,可以使用以下技术:

  • 对于框架版本 1.9 及更高版本,通过在队列对象的WDF_OBJECT_ATTRIBUTES结构中设置 WdfSynchronizationScopeQueue,为单个队列启用队列级同步。 此技术是首选技术。

  • 或者,可以在所有框架版本中使用以下步骤:

    1. 设备对象的WDF_OBJECT_ATTRIBUTES结构中,将 SynchronizationScope 设置为 WdfSynchronizationScopeQueue
    2. 为每个设备的队列对象使用默认WdfSynchronizationScopeInheritFromParent值。

如果不希望框架同步处理驱动程序 I/O 请求的回调函数,请使用驱动程序的驱动程序、设备和队列对象的默认 SynchronizationScope 值。 在这种情况下,框架不会自动同步驱动程序的 I/O 请求相关回调函数。 框架可以在 IRQL <= DISPATCH_LEVEL 调用回调函数。

设置 SynchronizationScope 值只会同步上一个表包含的回调函数。 如果希望框架还同步驱动程序的中断、DPC、工作项和计时器对象回调函数,请将这些对象的配置结构的 AutomaticSerialization 成员设置为 TRUE

只有当所有你想要同步的回调函数都在同一 IRQL 上运行时,才能将 AutomaticSerialization 设置为 TRUE。 选择下一步描述 的执行级别可能会导致 IRQL 级别不兼容。 在这种情况下,驱动程序必须使用 框架锁 ,而不是设置 AutomaticSerialization。 有关中断、DPC、工作项和计时器对象的配置结构的详细信息,以及有关在这些结构中设置 AutomaticSerialization 的限制的详细信息,请参阅 WDF_INTERRUPT_CONFIGWDF_DPC_CONFIGWDF_WORKITEM_CONFIGWDF_TIMER_CONFIG

如果将 AutomaticSerialization 设置为 TRUE,请选择队列级同步。

选择执行级别

当驱动程序创建某些类型的框架对象时,它可以指定对象的 执行级别 。 执行级别指定 IRQL,框架在其中调用处理驱动程序 I/O 请求的对象事件回调函数。

如果驱动程序提供执行级别,则提供的级别会影响队列和文件对象的回调函数。 通常,如果驱动程序使用自动同步,框架会在 IRQL = DISPATCH_LEVEL调用这些回调函数。 通过指定执行级别,驱动程序可以强制框架在 IRQL = PASSIVE_LEVEL调用这些回调函数。 框架在设置调用队列和文件对象回调函数的 IRQL 时使用以下规则:

  • 如果驱动程序使用自动同步,则框架会在 IRQL = DISPATCH_LEVEL 调用其队列和文件对象回调函数,除非驱动程序要求框架在 IRQL = PASSIVE_LEVEL 调用其回调函数。

  • 如果驱动程序不使用自动同步且未指定执行级别,则框架可以在 IRQL <= DISPATCH_LEVEL 调用驱动程序的队列和文件对象回调函数。

如果您的驱动程序提供文件对象回调函数,那么您可能会希望框架在 IRQL = PASSIVE_LEVEL 时调用这些回调函数,因为某些文件数据(如文件名)是可分页的。

若要提供执行级别,驱动程序必须为对象的WDF_OBJECT_ATTRIBUTES结构中的 ExecutionLevel 成员指定值。 驱动程序可以指定的执行级别值包括:

WdfExecutionLevelPassive
框架在 IRQL = PASSIVE_LEVEL调用对象的回调函数。

WdfExecutionLevelDispatch
框架可以在 IRQL <= DISPATCH_LEVEL 时调用对象的回调函数。 如果驱动程序使用自动同步,则框架始终在 IRQL = DISPATCH_LEVEL调用回调函数。

WdfExecutionLevelInheritFromParent
框架从对象的父级获取对象的 ExecutionLevel 值。

驱动程序对象的默认执行级别为 WdfExecutionLevelDispatch。 所有其他对象的默认执行级别为 WdfExecutionLevelInheritFromParent

有关执行级别值的详细信息,请参阅 WDF_EXECUTION_LEVEL

下表显示了 IRQL 级别,框架可以在其中为队列对象和文件对象调用驱动程序的回调函数。

同步范围 执行级别 队列和文件回调函数的IRQL

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= DISPATCH_LEVEL

可以将执行级别设置为 WdfExecutionLevelPassiveWdfExecutionLevelDispatch ,以便驱动程序、设备、文件、队列、计时器和常规对象。 对于其他对象,只能设置 WdfExecutionLevelInheritFromParent

指定 WdfExecutionLevelPassive 如果:

  • 驱动程序的回调函数必须调用框架方法或 Windows 驱动程序模型(WDM)例程,这些例程只能在 IRQL = PASSIVE_LEVEL调用。

  • 驱动程序的回调函数必须访问可分页代码或数据。 例如,文件对象回调函数通常访问可分页数据。

驱动程序可以设置 WdfExecutionLevelDispatch,并提供一个回调函数来创建工作项,以便在 IRQL = PASSIVE_LEVEL 时处理某些操作,而不是设置 WdfExecutionLevelPassive

在确定驱动程序是否应将对象的执行级别设置为 WdfExecutionLevelPassive 之前,请确定调用驱动程序堆栈中的驱动程序和其他驱动程序的 IRQL。 请考虑以下情况:

  • 如果驱动程序位于内核模式驱动程序堆栈的顶部,则系统通常会在 IRQL = PASSIVE_LEVEL 调用驱动程序。 此类驱动程序的客户端可能是基于 UMDF 的驱动程序或用户模式应用程序。 指定 WdfExecutionLevelPassive 不会对驱动程序的性能产生负面影响,因为框架无需将驱动程序的调用排队到在 IRQL = PASSIVE_LEVEL 执行的工作项。

  • 如果驱动程序不在堆栈的顶部,系统很可能不会在 IRQL = PASSIVE_LEVEL 调用驱动程序。 因此,框架必须将驱动程序对工作项的调用排入队列,这些调用将在稍后的 IRQL = PASSIVE_LEVEL 执行。 与允许在 IRQL <= DISPATCH_LEVEL 调用驱动程序的回调函数相比,此过程可能会导致驱动程序性能不佳。

对于 DPC 对象,对于不表示被动级计时器的计时器对象,如果将父设备的执行级别设置为 WdfExecutionLevelPassive,则无法将配置结构的 AutomaticSerialization 成员设置为 TRUE。 在 IRQL = PASSIVE_LEVEL 下,框架获取设备对象的 回调同步锁。 因此,它无法使用锁来同步 DPC 或计时器对象的回调函数,这些函数必须在 IRQL = DISPATCH_LEVEL 上执行。 在这种情况下,驱动程序应在必须彼此同步的任何设备、DPC 或计时器对象回调函数中使用 框架旋转锁

另请注意,对于表示被动级计时器的计时器对象,当父设备的执行级别设置为 WdfExecutionLevelPassive 时,才能将配置结构的 AutomaticSerialization 成员设置为 TRUE。