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 完成。 |

相关推荐
何中应1 分钟前
如何使用CMD命令启动应用程序(二)
windows·桌面应用·batch命令
sukalot1 小时前
windows C++-使用任务和 XML HTTP 请求进行连接(一)
c++·windows
ぃ扶摇ぅ2 小时前
Windows系统编程(三)进程与线程二
c++·windows
weixin_419349793 小时前
windows上安装python环境
windows
天上掉下来个程小白4 小时前
Stream流的中间方法
java·开发语言·windows
暮雪倾风4 小时前
【WPF开发】控件介绍-Grid(网格布局)
windows·wpf
sukalot5 小时前
windows C++-windows C++-使用任务和 XML HTTP 请求进行连接(二)
c++·windows
大道归简6 小时前
Docker 命令从入门到入门:从 Windows 到容器的完美类比
windows·docker·容器
没有余地 EliasJie7 小时前
Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT
pytorch·windows·深度学习·ubuntu·pycharm·conda·tensorflow
qxqxa7 小时前
cfg80211是怎么配置无线设备的AP的?
网络·驱动开发