執行驅動程式例程的 IRQL 會決定它可以呼叫的內核模式驅動程式支援例程。 例如,某些驅動程式支援例程需要呼叫端於 IRQL = DISPATCH_LEVEL 執行。 如果呼叫端在任何 IRQL 上執行且該 IRQL 高於 PASSIVE_LEVEL,則無法安全地呼叫其他方法或函數。
以下是呼叫最常實作標準驅動程式例程的 IRQL 清單。 IRQL 會從最低到最高優先順序列出。
被動層級
中斷屏蔽關閉 - 無。
驅動程式例程在 PASSIVE_LEVEL 呼叫 - DriverEntry、AddDevice、Reinitialize、Unload 例程、大部分分派例程、驅動程式建立的執行緒、背景工作執行緒回呼。
APC_LEVEL
中斷遮罩關閉 - APC_LEVEL 中斷已被遮罩。
在 APC_LEVEL 呼叫的驅動程式例程 - 某些分派例程(請參閱 分派例程和 IRQL)。
DISPATCH_LEVEL
中斷遮罩關閉 - DISPATCH_LEVEL 和 APC_LEVEL 中斷被遮罩。 可能會發生裝置、時鐘和電源故障中斷。
在 DISPATCH_LEVEL 呼叫的驅動程式例程 - StartIo、AdapterControl、AdapterListControl、ControllerControl、IoTimer、Cancel(同時持有取消微調鎖定)、DpcForIsr、CustomTimerDpc、CustomDpc 例程。
DIRQL
中斷遮罩關閉 - 驅動程序中斷物件的 IRQL <= DIRQL 的所有中斷。 具有較高 DIRQL 值的裝置中斷可能會發生,此外還有時鐘故障和電源故障中斷。
在 DIRQL 呼叫的驅動程式例程 - InterruptService、 SynchCritSection 例程。
APC_LEVEL 與 PASSIVE_LEVEL 間的唯一差異在於,執行於 APC_LEVEL 的程序無法取得 APC 中斷。 但兩個 IRQL 都表示線程內容,兩者都表示程式代碼可以分頁。
最底層驅動程式在三個 IRQL 之一運行時處理 IRP:
PASSIVE_LEVEL,在驅動程式的分派例程中,處理器上的中斷未被屏蔽。
DriverEntry、 AddDevice、 Reinitialize 和 Unload 例程也會在PASSIVE_LEVEL執行,如同任何驅動程式建立的系統線程一樣。
DISPATCH_LEVEL,在 StartIo 例程中,在處理器上遮罩DISPATCH_LEVEL和APC_LEVEL中斷
AdapterControl、AdapterListControl、ControllerControl、IoTimer 和 Cancel(當它持有取消旋轉鎖時),CustomTimerDpc 例程也在 DISPATCH_LEVEL 執行,DpcForIsr 和 CustomDpc 例程也是如此。
裝置 IRQL(DIRQL),在 ISR 和 SynchCritSection 例程中,處理器已經屏蔽掉驅動程式中斷物件的中斷,這些中斷的優先級小於或等於 SynchronizeIrql。
大部分較高層級的驅動程式在運行於某一個 IRQL 時會處理 IRP:
在驅動程式的分派例程中,當處理器沒有任何中斷被遮罩時,運行於PASSIVE_LEVEL級別。
DriverEntry、 Reinitialize、 AddDevice 和 Unload 例程也會在PASSIVE_LEVEL執行,就像任何驅動程式建立的系統線程或背景工作線程回呼例程或文件系統驅動程式一樣。
DISPATCH_LEVEL,當處理器上的DISPATCH_LEVEL和APC_LEVEL中斷被遮罩關閉時,在驅動程式的IoCompletion例程中
IoTimer、 Cancel 和 CustomTimerDpc 例程也會在 DISPATCH_LEVEL執行。
在某些情況下,大容量儲存裝置的中繼和最低層級驅動程式會在 IRQL APC_LEVEL 被呼叫。 特別是,這可能會發生在文件系統驅動程式傳送 IRP_MJ_READ 要求給較低驅動程式的頁面錯誤。
大部分的標準驅動程式例程都是在 IRQL 上執行,可讓他們直接呼叫適當的支援例程。 例如,裝置驅動程式必須在 IRQL DISPATCH_LEVEL執行時呼叫 AllocateAdapterChannel 。 由於大部分設備驅動器都會從 StartIo 例程呼叫這些例程,所以通常它們已在DISPATCH_LEVEL執行。
沒有 StartIo 例程的裝置驅動程式,因為它會設定及管理自己的 IRP 佇列,不一定會在DISPATCH_LEVEL IRQL 上執行,因為它應該呼叫 AllocateAdapterChannel。 這類驅動程式必須在呼叫 KeRaiseIrql 與 KeLowerIrql 之間嵌套呼叫 AllocateAdapterChannel,以確保在呼叫 AllocateAdapterChannel 時,能以所需的 IRQL 執行,並在呼叫例程恢復控制時,還原到原始的 IRQL。
呼叫驅動程序支援例程時,請注意下列事項。
呼叫 KeRaiseIrql 時,如果輸入的 NewIrql 值小於目前的 IRQL,會導致嚴重錯誤。 呼叫 KeLowerIrql ,除非是要還原原始的 IRQL (即在呼叫 KeRaiseIrql 之後),否則也會導致致命錯誤。
在 IRQL >= DISPATCH_LEVEL執行時,針對核心定義的發送器物件呼叫 KeWaitForSingleObject 或 KeWaitForMultipleObjects 以等候非零間隔會造成嚴重錯誤。
唯一可以安全等待事件、信號燈、互斥鎖或計時器設置為信號狀態的驅動程序例程,是在非任意線程上下文中於 IRQL PASSIVE_LEVEL 執行的例程,例如驅動程序建立的線程、DriverEntry 和 Reinitialize 例程,或是針對固有同步 I/O 操作(如大多數設備 I/O 控制請求)的分派例程。
執行於 IRQL PASSIVE_LEVEL 的情況下,可分頁的驅動程式碼不得呼叫 KeSetEvent、KeReleaseSemaphore 或 KeReleaseMutex,且輸入參數 Wait 設定為 TRUE。 這類呼叫可能會導致嚴重頁面錯誤。
任何在超過 IRQL APC_LEVEL 的狀態下執行的例程,無法從分頁集區分配記憶體或安全地存取分頁集區中的記憶體。 如果在 IRQL 上執行的例程大於 APC_LEVEL造成頁面錯誤,則這是嚴重錯誤。
驅動程式在 IRQL DISPATCH_LEVEL呼叫 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel 時必須執行。
當驅動程式呼叫 KeAcquireSpinLock 時,可以在 IRQL <= DISPATCH_LEVEL 執行,但必須呼叫 KeReleaseSpinLock 來釋放該旋轉鎖。 換句話說,呼叫KeReleaseSpinLockFromDpcLevel來釋放使用KeAcquireSpinLock取得的自旋鎖是屬於程式設計錯誤。
驅動程式不得在 IRQL > DISPATCH_LEVEL 執行時呼叫 KeAcquireSpinLockAtDpcLevel、KeReleaseSpinLockFromDpcLevel、KeAcquireSpinLock 或 KeReleaseSpinLock。
呼叫使用旋轉鎖的支援例程,例如 ExInterlockedXxx 例程時,如果呼叫端尚未在提高的 IRQL 上執行,則會在目前的處理器上提高 IRQL,提升到 DISPATCH_LEVEL 或 DIRQL。
在 IRQL > PASSIVE_LEVEL 下運行的驅動程式代碼應該儘快執行。 執行例程的 IRQL 愈高,整體效能就越重要,可微調該例程以儘快執行。 例如,任何呼叫 KeRaiseIrql 的驅動程式,只要可以,就應該立即對 KeLowerIrql 進行對等呼叫。
如需決定優先順序的詳細資訊,請參閱 排程、線程內容和 IRQL 白皮書。