驱动程序的 IRQL 注释

所有驱动程序开发人员都必须考虑中断请求级别 (IRQL) 。 IRQL 是介于 0 和 31 之间的整数;PASSIVE_LEVEL、DISPATCH_LEVEL和APC_LEVEL通常以符号方式引用,而其他则按其数值进行引用。 提高和降低 IRQL 应遵循严格的堆栈规则。 函数应旨在返回调用它的同一 IRQL。 堆栈中的 IRQL 值必须是非递减值。 函数不能在不首先提高 IRQL 的情况下降低 IRQL。 IRQL 注释旨在帮助强制实施这些规则。

当驱动程序代码具有 IRQL 注释时,代码分析工具可以更好地推断函数应运行的级别范围,并可以更准确地查找错误。 例如,可以添加注释,以指定可以调用函数的最大 IRQL;如果在更高的 IRQL 下调用函数,则代码分析工具可以识别不一致之处。

驱动程序函数应使用可能合适的 IRQL 信息进行批注。 如果其他信息可用,它将帮助代码分析工具对调用函数和被调用函数进行后续检查。 在某些情况下,添加批注是抑制误报的好方法。 可以在任何 IRQL 中调用某些函数(例如实用工具函数)。 在这种情况下,没有 IRQL 注释是正确的注释。

为 IRQL 批注函数时,考虑函数的演变方式(而不仅仅是其当前实现)尤为重要。 例如,实现的函数可能在比设计器预期的更高的 IRQL 下正常工作。 尽管根据代码实际操作对函数进行批注很诱人,但设计器可能会意识到未来的要求,例如需要降低最大 IRQL 以用于某些将来的增强功能或挂起的系统要求。 注释应派生自函数设计器的意图,而不是从实际实现中派生。

可以使用下表中的注释来指示函数及其参数的正确 IRQL。 IRQL 值在 Wdm.h 中定义。

IRQL 注释 说明
_IRQL_requires_max_ (irql) irql 是可以调用函数的最大 IRQL。
_IRQL_requires_min_ (irql) irql 是可以调用函数的最小 IRQL。
_IRQL_requires_ (irql) 必须在 irql 指定的 IRQL 处输入函数。
_IRQL_raises_ (irql) 函数在指定的 irql 处退出,但只能调用它来引发 (不会降低当前 IRQL) 。
_IRQL_saves_ 带批注的参数保存当前 IRQL,以便稍后还原。
_IRQL_restores_ 带批注的参数包含函数返回时要还原 IRQL_saves 的 IRQL 值。
_IRQL_saves_global_ (种类参数) 当前 IRQL 将保存到代码分析工具内部的位置,从中还原 IRQL。 此批注用于批注函数。 该位置按种类标识,并由参数进一步完善。 例如,OldIrql 可以是 类型,FastMutex 可以是保存旧 IRQL 值的参数。
_IRQL_restores_global_ (种类参数) 使用 IRQL_saves_global 注释的函数保存的 IRQL 将从代码分析工具内部的位置还原。
_IRQL_always_function_min_ () IRQL 值是函数可将 IRQL 降低到的最小值。
_IRQL_always_function_max_ () IRQL 值是函数可将 IRQL 提升到的最大值。
_IRQL_requires_same_ 带批注的函数必须在同一 IRQL 处进入和退出。 函数可以更改 IRQL,但它必须在退出之前将 IRQL 还原到其原始值。
_IRQL_uses_cancel_ 带批注的参数是应由DRIVER_CANCEL回调函数还原的 IRQL 值。 在大多数情况下,请改用 IRQL_is_cancel 注释。

DRIVER_CANCEL的批注

_IRQL_uses_cancel_和_IRQL_is_cancel_批注之间有区别。 _IRQL_uses_cancel_注释只是指定批注参数是应由DRIVER_CANCEL回调函数还原的 IRQL 值。 _IRQL_is_cancel_批注是一个复合批注,由_IRQL_uses_cancel_以及其他几个注释组成,这些注释可确保DRIVER_CANCEL回调实用工具函数的正确行为。 _IRQL_uses_cancel_注释本身只是偶尔有用;例如,如果 _IRQL_is_cancel_ 描述的其余义务已以其他方式履行。

IRQL 注释 说明
_IRQL_is_cancel_ 带批注的参数是作为调用DRIVER_CANCEL回调函数的一部分传入的 IRQL。 此批注指示函数是从 Cancel 例程调用的实用工具,它满足DRIVER_CANCEL函数的要求,包括释放取消旋转锁。

IRQL 批注的交互方式

IRQL 参数批注之间的交互性比其他批注多,因为 IRQL 值是由各种被调用函数设置、重置、保存和还原的。

指定最大和最小 IRQL

_IRQL_requires_max_和_IRQL_requires_min_注释指定不应从高于或低于指定值的 IRQL 调用函数。 例如,当 PREfast 看到一系列不更改 IRQL 的函数调用时,如果发现一个函数调用_IRQL_requires_max_值低于附近_IRQL_requires_min_,则会针对它遇到的第二次调用报告警告。 错误实际上可能发生在第一次调用中;消息指示冲突的另一半发生位置。

如果函数上的批注提及 IRQL 且未显式应用_IRQL_requires_max_,则代码分析工具将隐式应用注释_IRQL_requires_max_ (DISPATCH_LEVEL) ,这通常是在极少数情况下正确的。 将其隐式应用为默认值可消除大量注释混乱,并使异常更加明显。

始终隐含_IRQL_requires_min_ (PASSIVE_LEVEL) 批注,因为 IRQL 不能低于;因此,没有关于最低 IRQL 的相应显式规则。 除了 DISPATCH_LEVEL 之外,很少有函数同时具有上限,以及除 PASSIVE_LEVEL 之外的下限。

某些函数是在被调用函数无法安全地将 IRQL 提升到某个最大值或更常见的情况下,无法将其安全地降低到某个最小值的上下文中调用的。 _IRQL_always_function_max_和_IRQL_always_function_min_注释可帮助 PREfast 查找无意中发生这种情况的情况。

例如,DRIVER_STARTIO 类型的函数使用 _IRQL_always_function_min_ (DISPATCH_LEVEL) 进行批注。 这意味着在执行DRIVER_STARTIO函数期间,将 IRQL 降低到DISPATCH_LEVEL以下是错误的。 其他注释指示必须在DISPATCH_LEVEL时进入和退出函数。

指定显式 IRQL

使用_IRQL_raises_或_IRQL_requires_注释来帮助 PREfast 更好地报告通过_IRQL_requires_max_或_IRQL_requires_min_批注发现的不一致,因为它随后知道 IRQL。

_IRQL_raises_注释指示函数返回且 IRQL 设置为新值。 使用 _IRQL_raises_ 注释时,它还有效地将_drv_maxFunctionIRQL注释设置为相同的 IRQL 值。 但是,如果函数引发的 IRQL 高于最终值,然后将其降低到最终值,则应在_IRQL_raises_注释后面添加显式_IRQL_always_function_max_注释,以允许更高的 IRQL 值。

提高或降低 IRQL

_IRQL_raises_注释指示函数只能用于引发 IRQL,并且不能用于降低 IRQL,即使函数的语法允许它也是如此。 KeRaiseIrql 是不应用于降低 IRQL 的函数示例。

保存和还原 IRQL

使用_IRQL_saves_和_IRQL_restores_批注指示当前 IRQL (在批注参数中保存或还原到批注参数中是确切知道还是仅大约) 。

某些函数隐式保存和还原 IRQL。 例如,ExAcquireFastMutex 系统函数将 IRQL 保存在与第一个参数标识的快速互斥对象关联的不透明位置;保存的 IRQL 由该快速互斥对象的相应 ExReleaseFastMutex 函数还原。 若要显式指示这些操作,请使用_IRQL_saves_global_和_IRQL_restores_global_批注。 kindparam 参数指示保存 IRQL 值的位置。 保存值的位置不必精确指定,只要保存和还原值的注释是一致的。

维护相同的 IRQL

应使用 _IRQL_requires_same_ 注释或其他 IRQL 注释之一来批注驱动程序创建的任何函数更改 IRQL,以指示预期 IRQL 中的更改。 如果没有指示 IRQL 中任何更改的注释,则代码分析工具将为未在输入函数的同一 IRQL 处退出的任何函数发出警告。 如果打算在 IRQL 中进行更改,请添加相应的注释以禁止显示错误。 如果不打算更改 IRQL,则应更正代码。

保存和还原 I/O 取消例程的 IRQL

使用_IRQL_uses_cancel_批注指示批注参数是应由DRIVER_CANCEL回调函数还原的 IRQL 值。 此注释指示函数是从取消例程调用的实用工具,并且它完成了对DRIVER_CANCEL函数 (即,它履行调用方) 的义务。

例如,下面是DRIVER_CANCEL回调函数类型的声明。 其中一个参数是应由此函数还原的 IRQL。 批注指示取消函数的所有要求。

// Define driver cancel routine type.  //    
__drv_functionClass(DRIVER_CANCEL)  
_Requires_lock_held_(_Global_cancel_spin_lock_)  
_Releases_lock_(_Global_cancel_spin_lock_)  
_IRQL_requires_min_(DISPATCH_LEVEL)  
_IRQL_requires_(DISPATCH_LEVEL)  
typedef  
VOID  
DRIVER_CANCEL (  
    _Inout_ struct _DEVICE_OBJECT *DeviceObject,  
    _Inout_ _IRQL_uses_cancel_ struct _IRP *Irp  
    );  
  
typedef DRIVER_CANCEL *PDRIVER_CANCEL;  

驱动程序的 SAL 2.0 注释