使用框架工作项

工作项是驱动程序在 EvtWorkItem 事件回调函数中执行的任务。 这些函数在系统工作线程的上下文中以 IRQL = PASSIVE_LEVEL 异步运行。

如果以 IRQL = DISPATCH_LEVEL 运行的 EvtInterruptDpcEvtDpcFunc 函数必须在 IRQL = PASSIVE_LEVEL 执行其他处理,则基于框架的驱动程序通常使用工作项。

换句话说,如果以 IRQL = DISPATCH_LEVEL 运行的函数必须调用只能在 IRQL = PASSIVE_LEVEL 调用的函数,驱动程序可以使用工作项。

通常,驱动程序的 EvtInterruptDpcEvtDpcFunc 回调函数会创建一个工作项对象,并将其添加到系统的工作项队列中。 随后,系统工作线程将对象取消排队,并调用工作项的 EvtWorkItem 回调函数。

使用工作项的示例驱动程序

使用工作项的基于框架的示例驱动程序包括 1394、AMCC5933、PCIDRV 和 Toaster。

设置工作项

若要设置工作项,驱动程序必须:

  1. 创建工作项。

    驱动程序调用 WdfWorkItemCreate 来创建工作项对象并标识将处理工作项的 EvtWorkItem 回调函数。

  2. 存储有关工作项的信息。

    通常,驱动程序使用工作项对象的上下文内存来存储 有关 EvtWorkItem 回调函数应执行的任务的信息。 调用 EvtWorkItem 回调函数时,它可以通过访问此上下文内存来检索信息。 有关如何分配和访问上下文内存的信息,请参阅 Framework 对象上下文空间

  3. 将工作项添加到系统的工作项队列。

    驱动程序调用 WdfWorkItemEnqueue,它将驱动程序的工作项添加到工作项队列。

当驱动程序调用 WdfWorkItemCreate 时,它必须提供框架设备对象或框架队列对象的句柄。 当系统删除该对象时,它还会删除与该对象关联的任何现有工作项。 在调用父对象的 EvtCleanupCallback 回调之前,将释放工作项对象并清理其关联的工作项回调。

有关框架对象层次结构的清理规则的详细信息,请参阅 框架对象生命周期

使用Work-Item回调函数

将工作项添加到工作项队列后,它将保留在队列中,直到系统工作线程变得可用。 系统工作线程从队列中删除工作项,然后调用驱动程序的 EvtWorkItem 回调函数,将工作项对象作为输入传递。

通常, EvtWorkItem 回调函数执行以下步骤:

  1. 通过访问工作项对象的上下文内存,获取驱动程序提供的有关工作项的信息。

  2. 执行指定的任务。 如有必要,回调函数可以调用 WdfWorkItemGetParentObject 来确定工作项的父对象。

  3. 调用 WdfObjectDelete 以删除工作项对象,或者,如果驱动程序将重新排队工作项,则指示工作项的句柄现在可以重复使用。

每个工作项的回调函数执行的任务必须相对较短。 操作系统提供有限数量的系统工作线程,因此,如果驱动程序使用工作项回调函数执行耗时的任务,则可能会妨碍系统性能。

创建和删除工作项

驱动程序可以使用以下两种技术之一来创建和删除工作项:

  • 使用每个工作项一次:在需要时创建工作项,并在使用后立即将其删除。

    此方法适用于需要很少使用 (频率低于每分钟一次的驱动程序,) 少量工作项。

    例如,驱动程序的 EvtInterruptDpc 回调函数可以调用 WdfWorkItemCreate, 然后 WdfWorkItemEnqueue,工作项的 EvtWorkItem 回调函数可以调用 WdfObjectDelete

    如果驱动程序遵循此方案,并且其 EvtInterruptDpc 回调函数从 WdfWorkItemCreate 收到STATUS_INSUFFICIENT_RESOURCES返回值,则驱动程序必须能够推迟所需的工作,直到系统资源 (通常内存) 可用。

  • 创建驱动程序根据需要重新排队的一个或多个工作项。

    对于使用工作项频繁 (每分钟) 一次以上的驱动程序,或者驱动程序的 EvtInterruptDpc 回调函数无法轻松处理 来自 WdfWorkItemCreate 的STATUS_INSUFFICIENT_RESOURCES返回值,此方法非常有用。

    在驱动程序调用 WdfWorkItemEnqueue 之前,系统不会将工作线程分配给工作项。 因此,即使系统工作线程是有限的资源,在初始化设备时创建工作项会消耗少量内存,但不会影响系统性能。

    以下步骤描述了一种可能的方案:

    1. 驱动程序的 EvtDriverDeviceAdd 回调函数调用 WdfWorkItemCreate 以获取工作项句柄。
    2. 驱动程序的 EvtInterruptDpc 回调函数创建 EvtWorkItem 回调函数必须执行的操作列表,然后使用步骤 1 中的句柄调用 WdfWorkItemEnqueue
    3. 驱动程序的 EvtWorkItem 回调函数执行操作列表,并设置一个标志以指示回调函数已运行。

    随后,每次调用驱动程序的 EvtInterruptDpc 回调函数时,它都必须确定 EvtWorkItem 回调函数是否已运行。 如果 EvtWorkItem 回调函数尚未运行, 则 EvtInterruptDpc 回调函数不会调用 WdfWorkItemEnqueue,因为工作项仍处于排队状态。 在这种情况下, EvtInterruptDpc 回调函数仅更新 EvtWorkItem 回调函数的操作列表。

    每个工作项都与设备或队列相关联。 删除关联的设备或队列时,框架会删除所有关联的工作项,因此,如果使用此技术,驱动程序不必调用 WdfObjectDelete

一些驱动程序可能需要调用 WdfWorkItemFlush 以从工作项队列中刷新其工作项。 有关 WdfWorkItemFlush 的示例用法,请参阅方法的参考页。

如果驱动程序对未完成的工作项调用 WdfObjectDelete ,则结果取决于工作项的状态:

工作项状态 结果
已创建但未排队 立即清理工作项。
已排队 WdfObjectDelete 的调用将等待工作项完成执行,然后清理工作项
执行 如果驱动程序从同一线程) 上的 EvtWorkItem (调用 WdfObjectDelete则 WdfObjectDelete 将立即返回。 EvtWorkItem 完成后,将清理工作项。 否则, WdfObjectDelete 将等待 EvtWorkItem 完成。