I/O 要求の例 - 詳細
ファイル オブジェクトを開く図は、2 つの I/O スタック位置を持つ IRP を示していますが、IRP は、特定の要求を処理する階層化されたドライバーの数に応じて、任意の数の I/O スタック位置を持つことができます。
次の図は、ファイル オブジェクトを開く図のドライバーが I/O サポート ルーチン (IoXxx ルーチン) を使用して、読み取りまたは書き込み要求の IRP を処理する方法を詳しく示しています。
I/O マネージャーは、サブシステムの読み取り/書き込み要求に割り当てられている IRP を使用してファイル システム ドライバー (FSD) を呼び出します。 FSD は、IRP の I/O スタックの場所にアクセスして、実行する必要がある操作を決定します。
FSD は、I/O サポート ルーチン (IoAllocateIrp) を 1 回以上呼び出して追加の IRP を割り当てることで、元の要求をより小さな要求 (複数のデバイス ドライバーの場合) に分割できます。 追加の IRP が FSD に返され、下位ドライバーの I/O スタック位置が 0 になります。 FSD は、前の図に示すように追加の IRP を割り当てるのではなく、元の IRP を再利用できます。次の下位のドライバーの I/O スタック位置を元の IRP に設定し、下位のドライバーに渡します。
前の図の FSD は、ドライバーによって割り当てられた IRP ごとに、I/O サポート ルーチンを呼び出して、FSD が提供する完了ルーチンを登録します。完了ルーチンでは、FSD は、下位のドライバーが要求を満たしているかどうかを判断し、下位ドライバーが完了したときに各ドライバーに割り当てられた IRP を解放できます。 I/O マネージャーは、各ドライバーが割り当てられた IRP が正常に完了したか、エラー状態で完了したか、取り消されたかに関係なく、FSD が提供する完了ルーチンを呼び出します。 上位ドライバーは、下位ドライバーに対して割り当ておよび設定した IRP を解放する必要があります。 I/O マネージャーは、すべてのドライバーがそれらを完了した後に割り当てる IRP を解放します。
次に、FSD は I/O サポート ルーチン (IoGetNextIrpStackLocation) を呼び出して、次の下位ドライバーの要求を設定するために、次の下位ドライバーの I/O スタック位置にアクセスします (前の図では、次の下位ドライバーが最下位ドライバーになります)。次に、FSD は I/O サポート ルーチン (IoCallDriver) を呼び出して、その IRP を次の下位ドライバーに渡します。
IRP を使用して呼び出されると、最下位レベルのドライバーは、その I/O スタックの場所をチェックして、ターゲット デバイスで実行する必要がある操作 (IRP_MJ_XXX 関数コードで示されます) を決定します。 ターゲット デバイスは、指定された I/O スタックの場所にあるデバイス オブジェクトによって表され、IRP と共にドライバーに渡されます。 最下位レベルのドライバーは、IRP が I/O マネージャーによって所定のエントリポイント (ドライバーが IRP_MJ_XXX 操作 (ここでは IRP_MJ_READ または IRP_MJ_WRITE) のために定義したエントリ ポイント) にルーティングされ、かつ要求の他のパラメータの有効性が上位レベルのドライバーによってチェック済みと想定することができます。
上位レベルのドライバーがない場合、最下位レベルのドライバーは、IRP_MJ_XXX 操作の入力パラメーターが有効かどうかをチェックします。 その場合、ドライバーは通常、I/O サポート ルーチンを呼び出して、デバイス操作が IRP で保留中であることを I/O マネージャーに通知し、IRP をキューに入れるか、ターゲット デバイス (ここでは、物理デバイスまたは論理デバイス: ディスクまたはディスク上のパーティション) にアクセスする別のドライバー指定ルーチンに渡します。
I/O マネージャーは、ドライバーが既にターゲット デバイスの別の IRP の処理にビジー状態であるかどうかを判断し、IRP がある場合はキューに入れて戻ります。 そうでない場合、I/O マネージャーは、デバイスで I/O 操作を開始するドライバーが提供するルーチンに IRP をルーティングします (この段階では、前の図のドライバーと I/O マネージャーの両方が制御を返します)。
デバイスが割り込まれると、ドライバーの割り込みサービス ルーチン (ISR) は、デバイスの中断を停止し、操作に関する必要なコンテキストを保存するために必要な作業を行います。 その後、ISR は IRP を使用して I/O サポート ルーチン (IoRequestDpc) を呼び出して、ドライバーが提供する DPC (遅延プロシージャ 呼び出し) ルーチンをキューに入れ、ISR よりも低いハードウェア優先度で要求された操作を完了します。
ドライバーの DPC が制御を取得すると、コンテキスト (ISR の IoRequestDpc への呼び出しで渡される) を使用して I/O 操作を完了します。 DPC は、サポート ルーチンを呼び出して、次の IRP (存在する場合) をデキューし、その IRP をデバイスで I/O 操作を開始するドライバー指定のルーチンに渡します (手順 5 を参照)。 次に、DPC は IRP の I/O 状態ブロックで完了した操作に関する状態を設定し、IoCompleteRequest を使用して I/O マネージャーに返します。
I/O マネージャーは、IRP で最下位ドライバーの I/O スタック位置をゼロにし、FSD に割り当てられた IRP を使用してファイル システムの登録完了ルーチン (手順 3 を参照) を呼び出します。 この完了ルーチンチェック、要求を再試行するか、元の要求に関する内部状態メイン更新するか、ドライバーに割り当てられた IRP を解放するかを決定するために、I/O 状態ブロックをチェックします。 ファイル システムは、I/O 状態を設定し、元の IRP を完了できるように、下位レベルのドライバーに送信するすべてのドライバー割り当て IRP の状態情報を収集できます。 ファイル システムが元の IRP を完了すると、I/O マネージャーは I/O 操作の元の要求元 (サブシステムのネイティブ関数) に NTSTATUS 値を返します。
階層化されたドライバーの図の IRP の処理 に示されているファイル システム ドライバーと同様、既存のドライバーのチェーンに追加される新しいドライバーは、次のすべてを実行できます。
IRP に独自の完了ルーチンを設定します。 IoCompletion ルーチンは、I/O 状態ブロックをチェックして、下位ドライバーが IRP を正常に完了したか、IRP を取り消したか、エラーで完了したかを判断します。 完了ルーチンは、IRP を完了する前に、ドライバーが保存した可能性がある IRP 固有の状態を更新したり、ドライバーが割り当てた可能性がある操作固有のリソースを解放したりすることもできます。 さらに、完了ルーチンは、IRP の完了を延期できます (IRP でさらに多くの処理が必要であることを I/O マネージャーに通知します)、IRP の完了を許可する前に、次の下位レベルのドライバーに別の要求を送信できます。
割り当て先の IRP に次の下位レベルのドライバーの I/O スタック位置を設定し、次の下位レベルのドライバーに要求を送信します。
各 IRP で次の下位ドライバーの I/O スタック位置を設定し、IoCallDriver を呼び出すことによって、下位ドライバーに着信要求を渡します (主要な関数コード IRP_MJ_POWER を含む IRP では、ドライバーが PoCallDriver を使用する必要があります)。
ドライバーによって作成された各デバイス オブジェクトは、特定のドライバーが I/O 要求を実行する物理、論理、または仮想デバイスを表します。 デバイス オブジェクトの作成と設定の詳細については、「デバイス オブジェクトとデバイス スタック」を参照してください。
階層化ドライバーにおける IRP の処理の図にも示されているように、ほとんどのドライバーは、ドライバーが提供するシステム定義の標準ルーチンのセットを介して、各 IRP を段階的に処理しますが、チェーン内の異なるレベルのドライバーは、必然的に異なる標準ルーチンを持ちます。 たとえば、下位ドライバーのみが物理デバイスからの割り込みを処理するため、割り込み駆動型 I/O 操作を完了する ISR と DPC を持つのは、最下位レベルのドライバーのみです。 一方、このようなドライバーは、デバイスから割り込みを受け取ったときに I/O が完了したことを認識しているため、完了ルーチンは必要ありません。 この図の FSD のような 1 つ以上の完了ルーチンを持つのは、上位ドライバーのみです。