WDF驱动开发-工作项

工作项、线程和定时器是内核中用于执行长期任务的三种形态,其中工作项和线程本身没什么区别,只不过工作项对处理器的亲和性更好一些。工作项是驱动程序在 EvtWorkItem 事件回调函数中执行的任务。 这些函数在系统工作线程的上下文中以 IRQL = PASSIVE_LEVEL 异步运行。

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

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

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

设置工作项

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

  • 创建工作项:驱动程序调用 WdfWorkItemCreate 来创建工作项对象并标识将处理工作项的 EvtWorkItem 回调函数;
  • 存储有关工作项的信息:通常,驱动程序使用工作项对象的上下文内存来存储 有关 EvtWorkItem 回调函数应执行的任务的信息。 调用 EvtWorkItem 回调函数时,它可以通过访问此上下文内存来检索信息;
  • 将工作项添加到系统的工作项队列:驱动程序调用 WdfWorkItemEnqueue,它将驱动程序的工作项添加到工作项队列;

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

使用Work-Item回调函数

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

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

  • 通过访问工作项对象的上下文内存,获取驱动程序提供的有关工作项的信息;
  • 执行指定的任务。 如有必要,回调函数可以调用 WdfWorkItemGetParentObject 来确定工作项的父对象;
  • 调用 WdfObjectDelete 以删除工作项对象,或者,如果驱动程序将重新排队工作项,则指示工作项的句柄现在可以重复使用;

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

创建和删除工作项

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

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

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

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

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

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

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

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

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

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

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

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

一些驱动程序可能需要调用 WdfWorkItemFlush 以从工作项队列中刷新其工作项。

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

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

相关推荐
2202_754421545 小时前
一个计算频率的模块
驱动开发·fpga开发
系统之家装机大师6 小时前
Win11 22H2/23H2系统11月可选更新KB5046732发布!
windows·电脑
系统之家装机大师6 小时前
微软发布Win11 24H2系统11月可选更新KB5046740!
windows·电脑
戎梓漩8 小时前
windows下安装curl,并集成到visual studio
ide·windows·visual studio
蓝田~10 小时前
观察者模式和订阅模式
windows·观察者模式
梓仁沐白16 小时前
ubuntu+windows双系统切换后蓝牙设备无法连接
windows·ubuntu
九鼎科技-Leo20 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Yang.991 天前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
我不瘦但很逗1 天前
Windows下使用DBeaver连接云数据库(MySQL)
数据库·windows
ashane13141 天前
Java list
java·windows·list