ドライバーによって作成されたスレッドを使用したインタロック キューの管理

新しいドライバーでは、このセクションで説明する方法よりも、キャンセル セーフ IRP キュー フレームワークを優先して使用する必要があります。

システム フロッピー コントローラー ドライバーと同様に、デバイス専用スレッドを持つドライバーは、StartIo ルーチンではなく、通常、二重にリンクされたインターロック キューで IRP の独自のキューイング管理を行います。 ドライバーのスレッドは、デバイスで実行する必要のある作業がある場合に、インターロック キューから IRP を引き出します。

一般的に、ドライバーは、スレッドと他のドライバー ルーチンとの間で共有されるすべてのリソースに対するスレッドとの同期を管理する必要があります。 また、ドライバーは、IRP がキューに登録されていることを、ドライバーが作成したスレッドに通知する何らかの方法を備えている必要があります。 通常、スレッドは、ドライバーの Dispatch ルーチンが、インターロック キューに IRP を挿入した後にディスパッチャー オブジェクトを Signaled 状態に設定するまで、デバイス拡張に格納されているディスパッチャー オブジェクトで待機します。

ドライバーの Dispatch ルーチンが呼び出されると、各ルーチンは入力 IRP の I/O スタック位置のパラメーターをチェックし、それらが有効であれば、さらなる処理のために要求をキューに入れます。 ドライバー専用スレッドのキューに登録されている各 IRP に対して、Dispatch ルーチンは、ExInterlockedInsertXxx リスト を呼び出す前に、その IRP を処理するためにスレッドが必要とするコンテキストを設定する必要があります。 各 IRP のドライバーの I/O スタックの位置は、ドライバのスレッドにターゲット デバイス オブジェクトのデバイス拡張子へのアクセスを与え、スレッドが各 IRP をキューから削除する際に、ドライバはそのスレッドとコンテキスト情報を共有することができます。

キャンセル可能な IRP をキューイングするドライバーは、Cancel ルーチンを実装する必要があります。 IRP は非同期的にキャンセルされるため、ドライバーが結果的に競合状態になることを確実に回避する必要があります。 IRP のキャンセルに関連する競合状態とそれを回避する手法の詳細については、「IRP キャンセルの同期」を参照してください。

ドライバーによって作成されたすべてのスレッドは、IRQL = PASSIVE_LEVEL で実行され、ドライバーが PsCreateSystemThread を呼び出したときにあらかじめ設定された基本ランタイム優先度で実行されます。 スレッドが ExInterlockedRemoveHeadList を呼び出すと、IRP がドライバーの内部キューから削除されている間、現在のプロセッサで IRQL が一時的に DISPATCH_LEVEL に引き上げられます。 元の IRQL は、この呼び出しから戻ると PASSIVE_LEVEL に戻されます。

ドライバー スレッド (またはドライバーが指定したワーカー スレッド コールバック) は、実行される IRQL を慎重に管理する必要があります。 たとえば、次のことを考慮してください。

  • システム スレッドは通常 IRQL = PASSIVE_LEVEL で実行されるため、ドライバー スレッドが、カーネル定義ディスパッチャー オブジェクトがシグナル状態に設定されるのを待機することが可能です。

    たとえば、デバイス専用スレッドは、他のドライバーがイベントを満たし、スレッドが IoBuildSynchronousFsdRequest で設定するいくつかの部分転送 IRP を完了するまで待機することができます。

  • ただし、このようなデバイス専用スレッドは、特定のサポート ルーチンを呼び出す前に、現在のプロセッサで IRQL を上げる必要があります。

    たとえば、ドライバーが DMA を使用する場合、デバイス専用スレッドは、これらのルーチンと DMA 操作の特定のその他のサポート ルーチンを IRQL = DISPATCH_LEVEL で呼び出す必要があるため、KeRaiseIrqlKeLowerIrql の呼び出しの間に AllocateAdapterChannelFreeAdapterChannel の呼び出しを入れ子にする必要があります。

    StartIo ルーチンは DISPATCH_LEVEL で実行されるため、DMA を使用するドライバーは、StartIo ルーチンから KeXxxIrql ルーチンを呼び出す必要はありません。

  • ドライバーによって作成されたスレッドは、IRQL = PASSIVE_LEVEL で (それ自体が) 非任意スレッド コンテキストで実行されるため、ページング可能なメモリにアクセスできますが、他の多くの標準ドライバー ルーチンは IRQL >= DISPATCH_LEVEL で実行されます。 ドライバーによって作成されたスレッドが、このようなルーチンからアクセスできるメモリを割り当てる場合は、非ページ プールからメモリを割り当てる必要があります。 たとえば、デバイス専用スレッドが、ドライバーの ISR または SynchCritSectionAdapterControlAdapterListControlControllerControlDpcForIsrCustomDpcIoTimerCustomTimerDpc、または上位レベルのドライバーの IoCompletion ルーチンによって後でアクセスされるバッファーを割り当てた場合、スレッドが割り当てたメモリはページングできません。

  • ドライバーがデバイス拡張で共有状態情報またはリソースを保持する場合、ドライバーのスレッド (StartIo ルーチンのような) は、物理デバイスと共有データへのアクセスを、同じデバイス、メモリ位置、またはリソースにアクセスするドライバーの他のルーチンと同期させる必要があります。

    スレッドがデバイスまたは状態を ISR と共有する場合は、KeSynchronizeExecution を使用し、ドライバーが提供する SynchCritSection ルーチンを呼び出してデバイスをプログラミングするか、共有状態にアクセスする必要があります。 「クリティカル セクションの使用」を参照してください。

    スレッドが ISR 以外のルーチンと状態またはリソースを共有する場合は、ドライバーは、ドライバーによってストレージが提供される、ドライバーが初期化するエグゼクティブ スピン ロックで共有状態やリソースを保護する必要があります。 詳細については、「スピン ロック」を参照してください。

低速デバイスにドライバー スレッドを使用する場合の設計上のトレードオフの詳細については、「デバイスのポーリング」を参照してください。 「ハードウェアの優先度の管理」 も参照してください。 特定のサポート ルーチンの IRQL の詳細については、ルーチンのリファレンス ページを参照してください。