次の方法で共有


PIO でのダイレクト I/O の使用

DMA ではなくプログラミングされた I/O (PIO) を使用するドライバーは、ユーザー空間バッファーをシステム空間アドレス範囲に 2 倍にマップする必要があります。 次の図は、I/O マネージャーがダイレクト I/O を使用する PIO 転送操作の IRP_MJ_READ 要求を設定する方法を示しています。

diagram illustrating direct i/o for devices that use pio.

図は、PIO を使用するデバイスが同じタスクを処理する方法を示しています。

  1. ユーザー空間の仮想アドレスのある範囲は、現在のスレッドのバッファーを表し、そのバッファーの内容は実際には物理的に離れているページ数に格納される場合があります。 バッファー長が 0 以外の場合、I/O マネージャーは、このバッファーを記述する MDL を作成します。

  2. I/O マネージャーは、現在のスレッドの読み取り要求を処理します。この要求に対して、スレッドはバッファーを表すユーザー空間の仮想アドレスの範囲を渡します。

  3. I/O マネージャーまたは FSD は、ユーザーが指定したバッファーのアクセシビリティを確認します。 I/O マネージャーが MDL を作成した場合は、ユーザー バッファーの仮想アドレスの範囲を指定する MDL を使用して MmProbeAndLockPages を呼び出します。 MmProbeAndLockPages は、MDL 内の対応する物理アドレス範囲にも入力します。

  4. I/O マネージャーは、転送操作を要求する IRP の MDL (MdlAddress) へのポインターを提供します。 ドライバーが IRP を完了した後、I/O マネージャーまたはファイル システムが MmUnlockPages を呼び出すまで、MDL で説明されている物理ページはロックダウンされたままで、バッファーに割り当てられます。 ただし、このような MDL 内の仮想アドレスは、デバイス ドライバーまたはデバイス ドライバーの上に階層化される可能性がある中間ドライバーに IRP が送信される前であっても、非表示 (および無効) になる可能性があります。

  5. ドライバーでシステム (仮想) アドレスが必要な場合、ドライバーは、IRP の MdlAddress ポインターを使用して MmGetSystemAddressForMdlSafe を呼び出して、MDL 内のユーザー空間仮想アドレスをシステム空間のアドレス範囲に二重にマップします。 上の図では、AliasBuff は、二重にマップされたアドレスを記述する MDL を表しています。

  6. ドライバーは、二重にマップされた MDL (AliasBuff) からシステム空間の仮想アドレス範囲を使用して、メモリにデータを読み取ります。

ドライバーが IoCompleteRequest を呼び出して IRP を完了すると、ドライバーが MmGetSystemAddressForMdlSafe を呼び出した場合、I/O マネージャーまたはファイル システムは MDL の二重にマップされたシステム空間範囲を解放します。 I/O マネージャーまたはファイル システムは、MDL で説明されているページのロックを解除し、ドライバーの代わりに MDL と IRP を破棄します。 パフォーマンスを向上させるために、ドライバーは、仮想アドレスを使用する必要がない限り、手順 3 で説明されているように、MDL 物理アドレスをシステム空間に二重にマッピングしないようにする必要があります。 これにより、システム ページ テーブルエントリが不必要に使用され、ドライバーのパフォーマンスとスケーラビリティの両方が低下する可能性があります。 さらに、ほとんどの古いドライバーではこの状況を処理できないため、ページ テーブル エントリが不足すると、システムがクラッシュする可能性があります。

現在のユーザー スレッドのバッファーとスレッド自体は、そのスレッドが実行中の間のみ物理メモリに常駐することが保証されます。 前の図に示したスレッドの場合、別のプロセスのスレッドが実行されている間に、そのユーザー バッファーの内容がセカンダリ ストレージにページングされる可能性があります。 別のプロセスのスレッドが実行されると、メモリ マネージャーがロックダウンし、元のスレッドのバッファーを含む対応する物理ページを保持していない限り、要求元のスレッドのバッファーのシステム物理メモリが上書きされる可能性があります。

ただし、そのバッファーの元のスレッドの仮想アドレスは、メモリ マネージャーがバッファーの物理ページを保持している場合でも、別のスレッドが現在進行中の間は表示されません。 そのため、ドライバーは、MmGetMdlVirtualAddress によって返される仮想アドレスを使用してメモリにアクセスすることはできません。 このルーチンの呼び出し元は、パケット ベースのシステムまたはバス マスター DMA を使用してデータを転送するために、その結果を MapTransfer (IRP の MdlAddress ポインターと共に) 渡す必要があります。