系统工作线程

需要延迟处理的驱动程序可以使用 工作项,该工作项包含指向执行实际处理的驱动程序回调例程的指针。 驱动程序将工作项排队, 系统工作线程 从队列中删除工作项并运行驱动程序的回调例程。 系统维护这些系统工作线程的池,这些线程是系统线程,每个线程一次处理一个工作项。

驱动程序将 WorkItem 回调例程与工作项相关联。 当系统工作线程处理工作项时,它会调用关联的 WorkItem 例程。 在 Windows Vista 和更高版本的 Windows 中,驱动程序可以改为将 WorkItemEx 例程与工作项相关联。 WorkItemEx 采用的参数不同于 WorkItem 采用的参数。

WorkItemWorkItemEx 例程在系统线程上下文中运行。 如果驱动程序调度例程可以在用户模式线程上下文中运行,该例程可以调用 WorkItemWorkItemEx 例程来执行需要系统线程上下文的任何操作。

若要使用工作项,驱动程序会执行以下步骤:

  1. 分配和初始化新的工作项。

    系统使用 IO_WORKITEM 结构来保存工作项。 若要分配新的 IO_WORKITEM 结构并将其初始化为工作项,驱动程序可以调用 IoAllocateWorkItem。 在 Windows Vista 和更高版本的 Windows 中,驱动程序也可以分配自己的 IO_WORKITEM 结构,并调用 IoInitializeWorkItem 将结构初始化为工作项。 (驱动程序应调用 IoSizeofWorkItem 以确定保存工作项所需的字节数。)

  2. 将回调例程与工作项相关联,并将工作项排队,以便由系统工作线程处理。

    若要将 WorkItem 例程与工作项相关联并将工作项排队,驱动程序应调用 IoQueueWorkItem。 若要改为将 WorkItemEx 例程与工作项相关联并将工作项排队,驱动程序应调用 IoQueueWorkItemEx

  3. 不再需要工作项后,请将其释放。

    IoAllocateWorkItem 分配的工作项应由 IoFreeWorkItem 释放。 IoInitializeWorkItem 初始化的工作项必须先由 IoUninitializeWorkItem 取消初始化,然后才能将其释放。

    仅当工作项当前未排队时,才能取消初始化或释放工作项。 系统在调用工作项的回调例程之前将工作项取消排队,因此可以从回调中调用 IoFreeWorkItemIoUninitializeWorkItem

需要启动需要长时间处理的处理任务或发出阻止调用的 DPC 应将该任务的处理委托给一个或多个工作项。 当 DPC 运行时,会阻止所有线程运行。 此外,在 IRQL = DISPATCH_LEVEL 运行的 DPC 不得进行阻止调用。 但是,处理工作项的系统工作线程在 IRQL = PASSIVE_LEVEL 运行。 因此,工作项可以包含阻止调用。 例如,系统工作线程可以等待调度程序对象。

由于系统工作线程池是有限的资源, WorkItemWorkItemEx 例程只能用于需要短时间的操作。 如果其中一个例程运行时间过长 (如果它包含无限循环(例如,) 或等待太长),则系统可能会死锁。 因此,如果驱动程序需要长时间延迟处理,则应改为调用 PsCreateSystemThread 来创建自己的系统线程。

请勿调用 IoQueueWorkItemIoQueueWorkItemEx 将已加入队列的工作项排入队列。 这样做可能会导致系统数据结构损坏。 如果驱动程序在每次运行特定驱动程序例程时对同一工作项进行排队,则可以使用以下技术来避免第二次排队工作项(如果工作项已在队列中):

  • 驱动程序维护辅助角色例程的任务列表。
  • 此任务列表在提供给辅助角色例程的上下文中可用。 辅助角色例程和修改任务列表的任何驱动程序例程将同步其对列表的访问权限。
  • 每次运行辅助角色例程时,它都会执行列表中的所有任务,并在任务完成时从列表中删除每个任务。
  • 当新任务到达时,驱动程序会将此任务添加到列表中。 仅当任务列表以前为空时,驱动程序才会将工作项排队。

系统工作线程在调用工作线程之前从队列中删除工作项。 因此,一旦工作线程开始运行,驱动程序线程就可以安全地再次将工作项排队。