はじめに
今回はデバイスドライバの開発モデルについて整理していきます。
デバイスドライバの開発モデルには主にWindows Driver Model(WDM)とWindows Driver Foundation(WDF)の2種類が存在します。
この2つの開発モデルについてそれぞれ見ていきましょう。
WDM
WDMは、もともと2つあったWindowsの系譜であるWindows 9x系とWindows NT系のドライバ開発を一元化するために作られたデバイスドライバの開発モデルです。それぞれ、Windows 98とWindows 2000で導入されました。
Windows 9x系はMS-DOSをベースにしたOSであり、ドライバの開発モデルは仮想デバイスドライバ (Virtual Device Driver, VxD)と呼ばれるものでした。Windows NT系はMS-DOSとは異なる「New Technology」として開発され、ドライバの開発モデルはWindows NTカーネルモードドライバと呼ばれています。
2つのWindowsは、アプリケーションは同一のバイナリで動作することが可能でしたが、ドライバはそれぞれ別々のドライバモデルを使って開発する必要があり、デバイスメーカーは2つのドライバを作る必要がありました。そういった問題を解消するために2つのWindowsにおいて同一のバイナリが使用可能であるWDMが登場することになります。(それがうまくいったかは別の話です)
機能面としては電源管理やプラグ&プレイ、WMI(Windows Management Instrumentation)など、新しいハードウェア機能のサポートが行われたことも特徴の一つです。また、WDMは原則として前方互換を保つように設計されており、ドライバが作成されたOSよりも新しいバージョンのOSで動作することができます。(一部例外はあります)
WDMの階層構造
WDMではデバイスドライバの内部構造が階層化されており、階層ごとにDDI(Device Driver Interface)が定義され、役割が決まっています。各階層のドライバはこのDDIに合わせて、入出力要求パケット(I/O Request Packet, IRP)を使って相互に通信を行います。これによりドライバはその役割に対応した最小限の開発をすれば良くなります。
例えばUSBインターフェースの外部記憶装置クラスとしてUSBマスストレージクラスがあります。USBマスストレージクラスのサブクラスにはDiskDriveクラス(USBメモリなど)、CDROMクラス(DVD/CD-ROMドライブなど)などがあります。これらのクラスのDDIはSRBと呼ばれる構造体が定義されているため、共通のUSBマスストレージクラスのドライバを使用します。
さらにその下の階層のUSB Root HubへのインターフェースはURBという構造体が定義されているため、USBのクラスドライバは共通のインターフェースでUSB Root Hubのドライバを使用します。
WDF
WDFは、WDMの問題点を改善するために作られた、WDMのインターフェースのラッパーフレームワークです。
WDMには次のような問題点がありました。
・ ドライバモデルが低水準であり複雑である
・ 電源管理やプラグ&プレイが複雑でバグによりスリープや復帰に問題が出たり、プラグアウト時にBSODになりやすい
・ 定型的なコードを実装する必要があり、コード量が肥大化する
・ ほとんどのドライバはカーネルモードで実行する必要があり、純粋なユーザーモードドライバはサポートされていない
このような問題点を改善することを目標として、WDFが登場しました。
WDFの主な構成として、カーネルモードで動作するドライバ用のKernel Mode Driver Framework (KMDF)、ユーザーモードで動作するドライバ用のUser Mode Driver Framework (UMDF) 、それらをサポートする開発ツールから構成されます。Microsoft社ではドライバ開発においてWDFの使用が推奨されています。
この2つのフレームワークの紹介の前に、カーネルモードとユーザーモードについて説明したいと思います。
カーネルモードとユーザーモード
Windowsはプログラムの実行時の権限により、カーネルモードとユーザーモードに分かれています。
簡単に説明すると以下のような違いです。
-
カーネルモード
・OSのコアコンポーネントやカーネルモードドライバなどのカーネルモジュールが動作するモード
・PassiveLevelよりも高いIRQL(Interrupt Request Level)で動作することができる
・単一の仮想アドレス空間を共有し、複数のモジュールから同一のアドレスに参照することができる -
ユーザーモード
・アプリケーションやサービスなどのプロセスが動作するモード
・IRQLはPassiveLevelのみ動作することができる
(例外的に非同期処理をする特定のAPIが呼び出された場合はAPC_LEVELでも動作する)
・プロセス毎にプライベートな仮想アドレス空間が設定される
カーネルモードとユーザーモードにおける最大の違いは仮想アドレス空間の共有にあります。
カーネルモジュールは仮想アドレス空間を共有することで高速に処理ができます。
その反面、カーネルモードドライバに不具合があると、他のカーネルモジュールが確保したメモリを破壊し、そのメモリを参照したドライバがBSODを発生させます。
これに対して、ユーザーモードでは、プロセス毎にプライベートな仮想アドレス空間が設定され、プロセスに割り当てられたアドレス空間を超えることは出来ません。そのため、不正なメモリアクセスをした場合には、当該プロセスが異常終了するだけで済みます。
出典:https://docs.microsoft.com/ja-jp/windows-hardware/drivers/gettingstarted/user-mode-and-kernel-mode
KMDF
KMDFは基本的にはWDMの延長線上の技術であり、以下のような特徴あります。
・多くの機能をフレームワークによって処理することで実装するコード量の削減することができる
・フレームワークが抽象化レイヤーを提供し、電源管理とプラグ&プレイを処理する
・必要となる機能やイベント処理をオプトインする形で実装することができる
KMDFはオブジェクト指向プログラミングモデルを採用しています。一般的にオブジェクト指向と呼ばれるとC++やC#などのクラスをイメージしますが、クラスではなくC・C++言語による関数型のオブジェクト指向というのが特徴です。オブジェクトにはドライバ用のオブジェクトやデバイス用のオブジェクト、IOキュー用のオブジェクトなど複数のオブジェクトがあり、オブジェクトとそのオブジェクトに応じたオブジェクトメソッドやイベントコールバックにより開発を行います。また、各オブジェクトは他のオブジェクトに所属し、解放時に呼び出されるコールバックにて所属するオブジェクトを自動的に解放します。
KMDFは新規OSリリースのタイミングで更新されるため、今後リリースされるOSへの対応も容易です。また、KMDFに対応したMicrosoft提供の開発ツールが充実しており、開発者へのサポートも手厚いです。
UMDF
UMDFのドライバの構造は,KMDFとは異なります.UMDFのドライバは一般のアプリケーションと同じプロセスとして動作します。UMDFは実行されているプロセスのアドレス空間にしかアクセスができないため、不用意なメモリ空間にアクセスすることなく、システムの安定性を向上させることができます。
UMDFにはUMDF1(バージョン1)とUMDF2(バージョン2)があり、開発方法が大きく異なります。UMDF1はUMDF1用の独立したフレームワークが定義されており、COM(Component Object Module)のDLLとして実装します。UMDF2はKMDFと同等のフレームワークが使用できるようになり、特定の条件を満たせばUMDF2とKMDFの切り替えが可能です。(ソースコード互換)
参考ページ
https://docs.microsoft.com/ja-jp/windows-hardware/drivers/kernel/types-of-wdm-drivers#possible-driver-layers
https://sciencepark.co.jp/device_driver/dvdr/report-15/