デバイス ノードとデバイス スタック

Windows では、デバイスはプラグ アンド プレイ (PnP) デバイス ツリーのデバイス ノードによって表されます。 通常、I/O 要求がデバイスに送信されると、いくつかのドライバーが要求の処理に役立ちます。 これらのドライバーは、それぞれデバイス オブジェクトに関連付けられ、デバイス オブジェクトはスタックに配置されます。 デバイス オブジェクトとそれに関連付けられているドライバーのシーケンスは、デバイス スタックと呼ばれます。 各デバイス ノードには、独自のデバイス スタックがあります。

デバイス ノードとプラグ アンド プレイ デバイス ツリー

Windows では、プラグ アンド プレイ デバイス ツリー、または単にデバイス ツリーと呼ばれるツリー構造でデバイスを整理します。 通常、デバイス ツリー内のノードは、複合デバイス上のデバイスまたは個々の機能を表します。 ただし、一部のノードは、物理デバイスとの関連付けがないソフトウェア コンポーネントを表します。

デバイス ツリー内のノードは、デバイス ノードと呼ばれます。 デバイス ツリーのルート ノードは、ルート デバイス ノードと呼ばれます。 規則により、ルート デバイス ノードは、次の図に示すように、デバイス ツリーの下部に描画されます。

diagram of the device tree, showing device nodes.

デバイス ツリーは、PnP 環境に固有の親子関係を示しています。 デバイス ツリー内のいくつかのノードは、子デバイスが接続されているバスを表します。 たとえば、PCI バス ノードは、マザーボード上の物理 PCI バスを表します。 起動中、PnP マネージャーは PCI バス ドライバーに、PCI バスに接続されているデバイスを列挙するように求めます。 これらのデバイスは、PCI バス ノードの子ノードによって表されます。 上の図では、PCI バス ノードには、USB ホスト コントローラー、オーディオ コントローラー、PCI Express ポートなど、PCI バスに接続されている複数のデバイスを表す子ノードがあります。

PCI バスに接続されている一部のデバイスは、バス自体です。 PnP マネージャーは、これらの各バスに、接続されているデバイスを列挙するように求めます。 上の図では、オーディオ コントローラーが、オーディオ デバイスが接続されているバスであることがわかります。 PCI Express ポートはディスプレイ アダプターが接続されているバスであり、ディスプレイ アダプターは 1 台のモニターが接続されているバスであることがわかります。

ノードがデバイスとバスのどちらを表していると考えるかは、観点によって異なります。 たとえば、ディスプレイ アダプターは、画面に表示されるフレームを準備するための主要な役割を果たすデバイスと考えることができます。 ただし、ディスプレイ アダプターは、接続されているモニターを検出して列挙できるバスと考えることもできます。

デバイス オブジェクトとデバイス スタック

"デバイス オブジェクト" は、DEVICE_OBJECT 構造のインスタンスです。 PnP デバイス ツリー内の各デバイス ノードには、デバイス オブジェクトの順序付けられた一覧があり、これらの各デバイス オブジェクトはドライバーに関連付けられています。 デバイス オブジェクトの順序付けられた一覧と、オブジェクトに関連付けられているドライバーは、デバイス ノードのデバイス スタックと呼ばれます。

デバイス スタックは、いくつかの方法で考えることができます。 最も正式な意味では、デバイス スタックは (デバイス オブジェクト、ドライバー) ペアの順序付き一覧です。 ただし、特定のコンテキストでは、デバイス スタックをデバイス オブジェクトの順序付き一覧と考えると便利な場合があります。 他のコンテキストでは、デバイス スタックをドライバーの順序付き一覧と考えると便利な場合があります。

慣例により、デバイス スタックには上と下があります。 デバイス スタックに作成される最初のデバイス オブジェクトが一番下になり、デバイス スタックに作成されてアタッチされる最後のデバイス オブジェクトが一番上になります。

次の図では、Proseware Gizmo デバイス ノードに、3 つの (デバイス オブジェクト、ドライバー) ペアを含むデバイス スタックがあります。 一番上のデバイス オブジェクトはドライバー AfterThought.sys に関連付けられ、中央のデバイス オブジェクトはドライバー Proseware.sys に関連付けられ、一番下のデバイス オブジェクトはドライバー Pci.sys に関連付けられています。 図の中央にある PCI バス ノードには、2 つの (デバイス オブジェクト、ドライバー) ペアが含まれているデバイス スタックがあります (Pci.sys に関連付けられているデバイス オブジェクトと、Acpi.sys に関連付けられているデバイス オブジェクト)。

diagram showing device objects ordered in device stacks in the proseware gizmo and pci device nodes.

デバイス スタックはどのように構築されるか

起動中、PnP マネージャーは各バスのドライバーに、バスに接続されている子デバイスを列挙するように要求します。 たとえば、PnP マネージャーは PCI バス ドライバー (Pci.sys) に、PCI バスに接続されているデバイスを列挙するように要求します。 この要求に応じて、Pci.sys は、PCI バスに接続されている各デバイスのデバイス オブジェクトを作成します。 これらの各デバイス オブジェクトは、物理デバイス オブジェクト (PDO) と呼ばれます。 Pci.sys によって一連の PDO が作成された直後に、デバイス ツリーは次の図のようになります。

diagram of pci node and physical device objects for child devices.

PnP マネージャーは、デバイス ノードを新たに作成された各 PDO に関連付け、レジストリを調べて、ノードのデバイス スタックに含める必要があるドライバーを特定します。 デバイス スタックには 1 つの (1 つだけの) ファンクション ドライバーが必要であり、必要に応じて 1 つ以上のフィルター ドライバーを持つことができます。 ファンクション ドライバーは、デバイス スタックのメイン ドライバーであり、読み取り、書き込み、およびデバイス制御の要求を処理します。 フィルター ドライバーは、読み取り、書き込み、およびデバイス制御の要求の処理において、補助的な役割を果たします。 各ファンクション ドライバーとフィルター ドライバーは、読み込まれるとデバイス オブジェクトを作成し、自身をデバイス スタックにアタッチします。 ファンクション ドライバーによって作成されたデバイス オブジェクトはファンクション デバイス オブジェクト (FDO) と呼ばれ、フィルター ドライバーによって作成されたデバイス オブジェクトはフィルター デバイス オブジェクト (フィルター DO) と呼ばれます。 この時点で、デバイス ツリーは次の図のようになります。

diagram of a device tree showing the filter, function, and physical device objects in the proseware gizmo device node.

図では、1 つのノードでフィルター ドライバーがファンクション ドライバーの上にあり、もう一方のノードでフィルター ドライバーがファンクション ドライバーの下にあることに注意してください。 デバイス スタックでファンクション ドライバーの上にあるフィルター ドライバーは、上位フィルター ドライバーと呼ばれます。 ファンクション ドライバーの下にあるフィルター ドライバーは、下位フィルター ドライバーと呼ばれます。

PDO は、常にデバイス スタック内の最下位のデバイス オブジェクトです。 これは、デバイス スタックの構築方法に起因します。 PDO が最初に作成され、追加のデバイス オブジェクトは、スタックにアタッチされると、既存のスタックの一番上にアタッチされます。

デバイスのドライバーがインストールされると、インストーラーでは、情報 (INF) ファイルの情報を使って、どのドライバーがファンクション ドライバーで、どのドライバーがフィルター ドライバーかが判断されます。 通常、INF ファイルは Microsoft またはハードウェア ベンダーによって提供されます。 デバイスのドライバーがインストールされた後、PnP マネージャーは、レジストリを調べることによって、デバイスのファンクション ドライバーとフィルター ドライバーを決定できます。

バス ドライバー

上の図では、ドライバー Pci.sys が 2 つの役割を果たしていることがわかります。 まず、Pci.sys は PCI バス デバイス ノードの FDO に関連付けられます。 実際には、PCI バス デバイス ノードに FDO が作成されました。 したがって、Pci.sys は PCI バスのファンクション ドライバーです。 次に、Pci.sys は PCI バス ノードの各子の PDO に関連付けられます。 子デバイスの PDO が作成されたことを思い出してください。 デバイス ノードの PDO を作成するドライバーは、ノードのバス ドライバーと呼ばれます。

基準点が PCI バスである場合、Pci.sys はファンクション ドライバーです。 しかし、基準点が Proseware Gizmo デバイスの場合、Pci.sys はバス ドライバーになります。 このデュアル ロールは、PnP デバイス ツリーで一般的です。 バスのファンクション ドライバーとして機能するドライバーは、バスの子デバイスのバス ドライバーとしても機能します。

ユーザー モード デバイス スタック

ここまで、カーネル モードのデバイス スタックについて説明してきました。 つまり、スタック内のドライバーはカーネル モードで実行され、デバイス オブジェクトはシステム空間にマップされます。これは、カーネル モードで実行されているコードのみが使用できるアドレス空間です。 カーネル モードとユーザー モードの違いについては、「ユーザー モードとカーネル モード」を参照してください。

場合によっては、デバイスに、カーネル モードのデバイス スタックだけでなく、ユーザー モードのデバイス スタックもあります。 ユーザー モード ドライバーは、多くの場合、ユーザー モード ドライバー フレームワーク (UMDF) に基づいています。これは、Windows ドライバー フレームワーク (WDF) によって提供されるドライバー モデルの 1 つです。 UMDF では、ドライバーはユーザー モード DLL であり、デバイス オブジェクトは IWDFDevice インターフェイスを実装する COM オブジェクトです。 UMDF デバイス スタック内のデバイス オブジェクトは、WDF デバイス オブジェクト (WDF DO) と呼ばれます。

次の図は、USB-FX-2 デバイスのデバイス ノード、カーネル モード デバイス スタック、およびユーザー モード デバイス スタックを示しています。 ユーザー モード スタックとカーネル モード スタックの両方のドライバーは、USB-FX-2 デバイスに送信される I/O 要求に参加します。

diagram showing user-mode and kernel-mode device stacks.

すべてのドライバー開発者のための概念

ドライバー スタック