最下位レベル ドライバーでの IRP の処理

最下位レベルの物理ドライバーには、上位レベルのドライバーに必要のない特定の標準ルーチンがあります。 最下位レベルのドライバーの標準ルーチンのセットも、次の条件に従って異なります。

  • 各ドライバーが制御するデバイスの性質

  • ドライバーがダイレクト I/O またはバッファー I/O のデバイス オブジェクトを設定するかどうか

  • 個々のドライバーの設計

次の図は、標準ドライバー ルーチンの役割を示すために、最下位レベルの大容量記憶装置のドライバーによって処理されるときにサンプル IRP が通る可能性があるパスを示しています。 この図のドライバーには、次の特性があります。

  • デバイスは各 I/O 操作の終了時に割り込みを生成するため、このドライバーには ISR と DpcForIsr ルーチンがあります。

  • IRP の内部キューを設定して独自のキューを管理する代わりに、ドライバーには StartIo ルーチンがあります。

  • ドライバーは、システム DMA を使用するため、ダイレクト I/O のデバイス オブジェクトのフラグを設定し、AdapterControl ルーチンを持ちます。

diagram illustrating an irp path through lowest-level driver routines.

この図に示すように、I/O マネージャーは IRP を作成し、指定された主要な関数コードのドライバーのディスパッチ ルーチンに送信します。 関数コードが IRP_MJ_READ または IRP_MJ_WRITE のいずれかであれば、ディスパッチ ルーチンは DDDispatchReadWrite になります。

IoGetCurrentIrpStackLocation の呼び出し

IRP パラメーターを必要とするドライバー ルーチンは、IoGetCurrentIrpStackLocation を呼び出してドライバーの I/O スタックの場所を取得する必要があります。 このようなルーチンには、複数の主要な I/O 関数コード (IRP_MJ_*XXX) を処理するディスパッチ ルーチン、マイナー関数 (IRP_MN_XXX) をサポートする関数を処理するディスパッチ ルーチン、デバイス I/O 制御要求 (*IRP_MJ_DEVICE_CONTROLIRP_MJ_INTERNAL_DEVICE_CONTROL など) を処理するディスパッチ ルーチン、IRP を処理するその他すべてのドライバー ルーチンが含まれます。

このドライバーの I/O スタックの場所は最下位に位置し、不定数の上位レベルのドライバーの I/O スタックの場所は影付きで表示されています。 わかりやすくするために、DispatchReadWriteStartIoAdapterControlDpcForIsr ルーチンからの IoGetCurrentIrpStackLocation の呼び出しは、前の図には示していません。

IoMarkIrpPending と IoStartPacket の呼び出し

サンプル ドライバーは、ディスパッチ ルーチンで IRP を完了しませんが、代わりに、StartIo ルーチンで IRP を処理します。 これを行うためにあらかじめ、ディスパッチ ルーチンは IoMarkIrpPending を呼び出して、IRP がまだ完了していないことを示します。 次に、IoStartPacket を呼び出して、ドライバーの StartIo ルーチンでさらに処理するために IRP をキューに入れます。 ディスパッチ ルーチンは、NTSTATUS 値の STATUS_PENDING も返します。

次の図は、IoStartPacket の呼び出しを示しています。

diagram illustrating a call to iostartpacket.

ドライバーがデバイス上の別の IRP を処理していてビジー状態の場合、IoStartPacket は、デバイス オブジェクトに関連付けられているデバイス キューに IRP を挿入します。 必要に応じて、ドライバーは、デバイス キュー内の IRP にドライバーが決定した順序を適用する IoStartPacket に、Key 値をパラメーターとして提供できます。

ドライバーがビジー状態ではなく、デバイス キューが空の場合、I/O マネージャーは直ちに StartIo ルーチンを呼び出し、入力 IRP を渡します。

大容量記憶装置の場合、最下位レベルのドライバーは、次の 2 つの理由から IoStartPacket を呼び出すときに Cancel ルーチンを提供する必要はありません。

  1. このようなドライバーの上に階層化されたファイル システムは通常、ファイル I/O 要求のキャンセルを処理します。

  2. 大容量記憶装置のドライバーは、IRP をすばやく処理します。

通常、階層化されたドライバーのチェーンで最上位レベルのドライバーは、IRP のキャンセルを処理します。

AllocateAdapterChannel と MapTransfer の呼び出し

最下位レベルのドライバー ルーチンを介した IRP パスを説明する図に示した StartIo ルーチンが、転送要求を 1 回の DMA 操作で実行できることを判断すると仮定すると、StartIo ルーチンは、ドライバーの AdapterControl ルーチンと IRP のエントリ ポイントを使用して AllocateAdapterChannel を呼び出します。

システム DMA コントローラーが使用可能な場合、I/O マネージャーはドライバーの AdapterControl ルーチンを呼び出して転送操作を設定します。 AdapterControl ルーチンは、MapTransfer を呼び出してシステム DMA コントローラーを設定します。 次に、ドライバーは、DMA 操作に関してデバイスをプログラミングし、復帰します。 (DMA およびアダプター オブジェクトの使用の詳細については、「入力/出力手法」を参照してください。)

ドライバーの ISR からの IoRequestDpc の呼び出し

転送操作が完了したことを示すためにデバイスが割り込むと、最下位レベルのドライバー ルーチンを介した IRP パスを説明する図に示すように、ドライバーの ISR はデバイスが割り込みを生成するのを阻止し、IoRequestDpc を呼び出します。

この呼び出しにより、ドライバーの DpcForIsr ルーチンがキューに入り、より低いハードウェア優先度 (IRQL) で転送操作を可能な限り多く完了させます。

IoStartNextPacket と IoCompleteRequest の呼び出し

DpcForIsr ルーチンは、転送の処理を完了すると、ドライバーの StartIo ルーチンがデバイス キュー内の次の IRP がある場合はその IRP で呼び出されるように、IoStartNextPacket を直ちに呼び出します。 また、DpcForIsr ルーチンは、完了したばかりの IRP の I/O 状態ブロックを設定して、IRP の IoCompleteRequest を呼び出します。

次の図は、IoStartNextPacketIoCompleteRequest に対するこのドライバーの呼び出しを示しています。

calling iostartnextpacket and iocompleterequest.

ドライバーは、IoStartNextPacket または IoStartNextPacketByKey を呼び出して、望ましくは IoCompleteRequest を呼び出す前に、次に要求された I/O 操作をできるだけ早く開始する必要があります。

デバイスに対する IRP がキューに入っている場合、IoStartNextPacketKeRemoveDeviceQueue を呼び出して、キューから次の IRP を削除します。 その後、I/O マネージャーはドライバーの StartIo ルーチンを呼び出し、デキューされた IRP を渡します。 現在デバイス キューに IRP が存在しない場合、IoStartNextPacket は呼び出し元に復帰するだけです。

IRP での I/O 状態ブロックの設定

すべての最下位レベルのドライバーは、IoCompleteRequest を呼び出す前に IRP の I/O 状態ブロックを設定する必要があります。 (前の図では、2 つ目の影付き領域はステータス ブロックを示しています)。I/O 状態ブロックは、上位レベルのドライバーに情報を提供し、最終的には、I/O 操作の最初の要求元に情報が提供されます。 前の図のドライバーの上に階層化された上位レベルのドライバーでは、このドライバーによって設定された I/O 状態ブロックを読み取る IoCompletion ルーチンが設定されている可能性があります。 上位レベルのドライバーは通常、デバイス ドライバーが完了した IRP の I/O 状態ブロックを変更することはありません。ただし、上位レベルのドライバーが IRP を再試行している場合を除きます (この場合は、I/O 状態ブロックが再初期化されます)。

次の下位ドライバーに IRP を送信せずに IRP を完了するすべての上位レベルのドライバーは、IoCompleteRequest を呼び出す前に、その IRP の I/O 状態ブロックを設定することも必要です。 全体的な I/O スループットを向上させるには、上位レベルのドライバーが各 IRP の独自の I/O スタックの場所にあるパラメーターをチェックする必要があります。パラメーターが無効な場合は、I/O 状態ブロックを設定し、要求自体を完了する必要があります。 ドライバーは、可能な限り、チェーン内で下位ドライバーに無効な要求を渡さないようにする必要があります。

前の図の転送操作が成功したと仮定すると、DpcForIsr ルーチンは、最下位レベルのドライバー ルーチンを介した IRP パスを説明する図に示すように、IRP の I/O 状態ブロックの Status に STATUS_SUCCESS を、Information に転送されたバイト数を設定します。

標準ドライバー ルーチンの多くは、NTSTATUS 型の値も返します。 STATUS_SUCCESS などの NTSTATUS 定数の詳細については、「エラーのログ記録」を参照してください。