ハードウェアの優先度の管理
ドライバー ルーチンが実行される IRQL によって、呼び出すことができるカーネル モード ドライバーサポート ルーチンが決まります。 たとえば、いくつかのドライバー サポート ルーチンでは、呼び出し元が IRQL = DISPATCH_LEVEL で実行されている必要があります。 また、呼び出し元が PASSIVE_LEVEL より高い IRQL で実行されている場合は、安全に呼び出すことができないものもあります。
最も一般的に実装されている標準ドライバー ルーチンが呼び出される IRQL の一覧を次に示しています。 IRQL は、優先度の低いものから高いものの順に一覧表示されています。
PASSIVE_LEVEL
割り込みのマスクオフ — なし。
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 もスレッド コンテキストを意味し、どちらもコードのページアウトが可能であることを意味します。
最下位レベルのドライバーは、次の 3 つの IRQL のいずれかで実行中に IRP を処理します。
ドライバーのディスパッチ ルーチンで、割り込みをプロセッサでマスクオフしない PASSIVE_LEVEL
DriverEntry、AddDevice、Reinitialize、Unload ルーチンも、ドライバーによって作成されたシステム スレッドと同様に、PASSIVE_LEVEL で実行されます。
StartIo ルーチンで、DISPATCH_LEVEL 割り込みと APC_LEVEL 割り込みがプロセッサでマスク オフされる DISPATCH_LEVEL
AdapterControl、AdapterListControl、ControllerControl、IoTimer、Cancel (スピン ロックのキャンセルを保持しているとき)、および CustomTimerDpc ルーチンも、DpcForIsr および CustomDpc ルーチンと同様に、DISPATCH_LEVEL で実行されます。
ISR および SynchCritSection ルーチンで、ドライバの割り込みオブジェクトの SynchronizeIrql 以下のすべての割り込みがプロセッサでマスク オフされる、デバイス IRQL (DIRQL)
最も上位レベルのドライバーは、次の 2 つの IRQL のいずれかで実行中に IRP を処理します。
ドライバーのディスパッチ ルーチンで、割り込みをプロセッサでマスクオフしない PASSIVE_LEVEL
DriverEntry、Reinitialize、AddDevice、Unload ルーチンも、ドライバーによって作成されたシステム スレッドまたはワーカー スレッドのコールバック ルーチンまたはファイル システム ドライバーと同様に、PASSIVE_LEVEL で実行されます。
ドライバーの IoCompletion ルーチンで、DISPATCH_LEVEL 割り込みと APC_LEVEL 割り込みがプロセッサでマスク オフされる DISPATCH_LEVEL
IoTimer、Cancel、CustomTimerDpc ルーチンも、DISPATCH_LEVEL で実行されます。
状況によっては、大容量記憶装置の中間レベルおよび最下位レベルのドライバーが IRQL APC_LEVEL で呼び出されます。 特に、これは、ファイル システム ドライバーが下位ドライバーに IRP_MJ_READ 要求を送信するページ フォールトで発生することがあります。
ほとんどの標準ドライバー ルーチンは、適切なサポート ルーチンを単純に呼び出すことができる IRQL で実行されます。 たとえば、デバイス ドライバーは、IRQL DISPATCH_LEVEL で実行中に AllocateAdapterChannel を呼び出す必要があります。 ほとんどのデバイス ドライバーは、StartIo ルーチンからこれらのルーチンを呼び出すため、通常、これらは既に DISPATCH_LEVEL で実行されています。
StartIo ルーチンがないデバイス ドライバーは、それ自身が IRP のキューを設定して管理するため、AllocateAdapterChannel を呼び出す必要がある場合に、必ずしも DISPATCH_LEVEL IRQL で実行されているわけではないことに注意してください。 このようなドライバーは、KeRaiseIrql と KeLowerIrql の呼び出しの間に AllocateAdapterChannel の呼び出しを入れ子にすることで、AllocateAdapterChannel を呼び出すときに必要な IRQL で実行し、呼び出し元のルーチンが制御を回復したときに元の IRQL に戻るようにする必要があります。
ドライバー サポート ルーチンを呼び出すときは、次の点に注意してください。
現在の IRQL より小さい入力 NewIrql 値で KeRaiseIrql を呼び出すと、致命的なエラーが発生します。 元の IRQL を復元する場合以外 (つまり、KeRaiseIrql を呼び出した後) に KeLowerIrql を呼び出すと、これも致命的なエラーとなります。
IRQL >= DISPATCH_LEVEL で実行しているときに、カーネル定義ディスパッチャー オブジェクトが 0 以外の間隔を待機するために KeWaitForSingleObject または KeWaitForMultipleObjects を呼び出すと、致命的なエラーになります。
イベント、セマフォ、ミューテックス、またはタイマーがシグナル状態に設定されるのを安全に待機できるドライバー ルーチンは、IRQL PASSIVE_LEVEL で非固定スレッド コンテキストで実行されるルーチンのみです (ドライバーによって作成されたスレッド、DriverEntry ルーチンと Reinitialize ルーチン、本質的に同期的な I/O 操作 (ほとんどのデバイス I/O 制御要求など) のディスパッチ ルーチンなど)。
IRQL PASSIVE_LEVEL で実行中でも、ページング可能なドライバー コードは、入力 Wait パラメーターを TRUE に設定して KeSetEvent、KeReleaseSemaphore、または KeReleaseMutex を呼び出してはなりません。 このような呼び出しにより、致命的なページ エラーを引き起こす可能性があります。
IRQL APC_LEVEL より大きい値で実行されているルーチンは、ページ プールからメモリを割り当てることも、ページ プール内のメモリに安全にアクセスすることもできません。 IRQL で実行されているルーチンが APC_LEVEL より大きい場合は、ページ フォールトが発生します。これは致命的なエラーです。
ドライバーは、KeAcquireSpinLockAtDpcLevel と KeReleaseSpinLockFromDpcLevel を呼び出すときに、IRQL DISPATCH_LEVEL で実行されている必要があります。
ドライバーは、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 に対して逆呼び出しを行う必要があります。
優先順位の決定の詳細については、「Scheduling, Thread Context, and IRQL (スケジュール、スレッド コンテキスト、IRQL)」に関するホワイト ペーパーを参照してください。