共用方式為


驅動程式的 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保存_ 註解參數會儲存目前的 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_ 批註參數是 IRQL 值,應該由 DRIVER_CANCEL 回呼函式還原。 在大部分情況下,請改用 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_ 批註來指出批註參數是 IRQL 值,應該由 DRIVER_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 註釋