驅動程式的 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 註釋 | Description |
---|---|
_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 的程式代碼分析工具內部的位置。 這個批註是用來標註函式。 位置會依種類識別,並由 param 進一步精簡。 例如,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 註釋 | 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_min_下方_IRQL_requires_max_值的函式呼叫,則會在遇到第二次呼叫時回報警告。 錯誤可能會在第一次呼叫中實際發生;訊息指出衝突的另一半發生位置。
如果函式上的註釋提及 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 儲存在與第一個參數所識別之快速 mutex 物件相關聯的不透明位置;儲存的 IRQL 會由該快速 mutex 對象的對應 ExReleaseFastMutex 函式還原。 若要明確指出這些動作,請使用_IRQL_saves_global_和_IRQL_restores_global_批注。 kind 和 param 參數會指出 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;