需要延迟处理的驱动程序可以使用 工作项,该项包含指向执行实际处理的驱动程序回调例程的指针。 驱动程序将工作项排在队列中, 系统工作线程 从队列中删除工作项,并运行驱动程序的回调例程。 系统维护这些系统工作线程的池,这些线程是一次处理一个工作项的系统线程。
驱动程序将 WorkItem 回调例程与工作项相关联。 当系统工作线程处理工作项时,它会调用关联的 WorkItem 例程。 在 Windows Vista 和更高版本的 Windows 中,驱动程序可以改为将 WorkItemEx 例程与工作项相关联。 WorkItemEx 采用的参数,这些参数不同于 WorkItem 采用的参数。
WorkItem 和 WorkItemEx 例程在系统线程上下文中运行。 如果驱动程序调度例程可以在用户模式线程上下文中运行,该例程可以调用 WorkItem 或 WorkItemEx 例程来执行需要系统线程上下文的任何作。
若要使用工作项,驱动程序将执行以下步骤:
分配并初始化新的工作项。
系统使用 IO_WORKITEM 结构来保存工作项。 若要分配新的 IO_WORKITEM 结构并将其初始化为工作项,驱动程序可以调用 IoAllocateWorkItem。 在 Windows Vista 和更高版本的 Windows 中,驱动程序也可以分配自己的 IO_WORKITEM 结构,并调用 IoInitializeWorkItem 将结构初始化为工作项。 (驱动程序应调用 IoSizeofWorkItem 来确定保存工作项所需的字节数。
将回调例程与工作项相关联,并将工作项排在队列中,以便由系统工作线程处理。
若要将 WorkItem 例程与工作项相关联,并将工作项排入队列,驱动程序应调用 IoQueueWorkItem。 若要改为将 WorkItemEx 例程与工作项相关联,并将工作项排入队列,驱动程序应调用 IoQueueWorkItemEx。
不再需要工作项后,请释放它。
IoAllocateWorkItem 分配的工作项应由 IoFreeWorkItem 释放。 IoInitializeWorkItem 初始化的工作项必须先由 IoUninitializeWorkItem 取消初始化,然后才能释放它。
仅当工作项当前未排队时,才能取消初始化或释放工作项。 系统在调用工作项的回调例程之前将工作项移出队列,因此可以从回调中调用 IoFreeWorkItem 和 IoUninitializeWorkItem 。
需要启动需要长时间处理或进行阻塞调用的处理任务的 DPC 应将该任务的处理委托给一个或多个工作项。 DPC 运行时,会阻止所有线程的运行。 此外,在 IRQL = DISPATCH_LEVEL 运行的 DPC 不得进行阻塞调用。 但是,处理工作项的系统工作线程在IRQL = PASSIVE_LEVEL运行。 因此,工作项可以包含阻塞调用。 例如,系统工作线程可以等待调度程序对象。
由于系统工作线程池是有限的资源, WorkItem 和 WorkItemEx 例程只能用于需要短时间的作。 如果其中一个例程运行时间过长(例如,如果它包含无限循环),或等待太长,则系统可能会死锁。 因此,如果驱动程序需要长时间的延迟处理,则应改为调用 PsCreateSystemThread 来创建自己的系统线程。
不要调用 IoQueueWorkItem 或 IoQueueWorkItemEx 来对队列中已有的工作项进行排队。 这样做可能会导致系统数据结构损坏。 如果每次运行特定驱动程序例程时,驱动程序将同一工作项入队列,则可以使用以下技术避免该工作项被重复加入队列:
- 驱动程序维护工作例程的任务列表。
- 此任务列表在提供给工作例程的上下文中可以使用。 工作例程和修改任务列表的任何驱动程序例程将同步其对列表的访问权限。
- 每次运行辅助角色例程时,它都会执行列表中的所有任务,并在任务完成时从列表中删除每个任务。
- 当新任务到达时,驱动程序会将此任务添加到列表中。 仅当任务列表以前为空时,驱动程序才会将工作项排队。
系统工作线程在调用工作线程之前从队列中删除工作项。 因此,一旦工作线程开始运行,驱动程序线程就可以再次安全地对工作项进行排队。