驱动程序的 IRQL 注释

所有驱动程序开发人员都必须考虑中断请求级别(IRCL)。 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 注释 DESCRIPTION
_IRQL_requires_max_(irql irql 是函数可在其上调用的最大 IRQL。
_IRQL_requires_min_(irql irql 是可以调用函数的最小 IRQL。
_IRQL_requires_(irql 必须在 irql 指定的 IRQL 处输入该函数。
_IRQL_raises_(irql 函数在指定的 irql 处退出,但只能调用它来提升当前 IRQL(不能降低)。
_IRQL_保存_ 带批注的参数保存当前 IRQL,以便稍后还原。
_IRQL_restores_ 带批注的参数包含一个将由 IRQL_saves 还原的 IRQL 值,该值将在函数返回时恢复。
_IRQL_saves_global_(种类参数 当前 IRQL 被保存到用于还原 IRQL 的代码分析工具的内部位置。 此注释用于标记函数。 该位置由类型标识,并通过参数进一步细化。 例如,OldIrql 可以是类型,FastMutex 可以是保存该旧 IRQL 值的参数。
_IRQL_restores_global_(kind, param 使用IRQL_saves_global注释的函数保存的 IRQL 将从代码分析工具内部的位置还原。
_IRQL_always_function_min_( IRQL 值是函数能够将 IRQL 降低到的最小值。
_IRQL_always_function_max_(value 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 注释 DESCRIPTION
_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_批注。 类型参数和参数指示保存 IRQL 值的位置。 保存值的位置不必精确指定,只要保存和还原值的批注是一致的。

维护相同的 IRQL

您应该批注驱动程序创建的任何函数,在这些函数中 IRQL 状态会发生变化,并使用_IRQL_requires_same_批注或其他 IRQL 批注之一来表明这种变化是预期的。 如果缺少指示 IRQL 中任何更改的注释,则代码分析工具将针对未在输入函数的同一 IRQL 处退出的任何函数发出警告。 如果 IRQL 的变更是有意的,请添加适当的批注以抑制错误。 如果不打算更改 IRQL,则应更正代码。

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

使用 _IRQL_uses_cancel_ 批注来指示该批注参数是应由 DRIVER_CANCEL 回调函数还原的 IRQL 值。 此批注表明该函数是从 cancel 例程中调用的一个实用工具,它满足了对 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 批注