Archive for 2008/07/22

カーネルからWinSock

カーネルモードで動作するデバイスドライバでは、アプリケーションから使える API を同じように呼び出すことは、できません。一般に、呼び出せる API には制限が加わります。ここで「API」と書きましたが、デバイスドライバから呼び出す関数やルーチンを “API”(Application Programming Interface) と呼ぶのは、適切ではありませんね。「システム関数」や「システムルーチン」と呼ぶべきなのかも知れません。

さて、デバイスドライバを実装する際に、アプリケーションと同じシステム関数を呼び出したい場合があります。たとえば、デバイスドライバから GUI 表示を行いたい場合や、TCP/IP 通信などのネットワーク入出力を行いたい場合です。WinCE 6.0 では、このような場合、そのデバイスドライバを、カーネル・モード・ドライバではなく、ユーザ・モード・ドライバとして動かすというのが、最も安直でしょう。

デバイスドライバをユーザ・モード・ドライバとして動かす場合、そのドライバに対する、.bib ファイルの MODULES セクションの記述と .reg ファイルのレジストリ項目記述による指定が必要です。.bib ファイルの MODULES セクションでは、Type エントリに記述するフラグから K を外すか、または、K の代わりに Q を指定しなければいけません。MSDN のリファレンスの、
 http://msdn.microsoft.com/en-us/library/aa908680.aspx
 http://msdn.microsoft.com/en-us/library/aa909662.aspx
が参考になるでしょう。.reg ファイルのレジストリ項目では、そのドライバのキーの Flags 値に 0×10(EVFLAGS_LOAD_AS_USERPROC)を設定します。これについては、MSDN のリファレンスの、
 http://msdn.microsoft.com/en-us/library/aa930532.aspx
に説明があります。

しかし、ユーザ・モード・ドライバの場合、アプリケーションからドライバを呼び出す際のオーバーヘッドが大きいという短所があります。カーネル・モード・ドライバの場合とは異なり、ユーザ・モード・ドライバの呼び出しでは、システムコールと Device Manager を介した先に、さらに User Mode Driver Reflector と User Mode Driver Host が介在するからです。この様子は、MSDN のリファレンスの
 http://msdn.microsoft.com/en-us/library/aa932450.aspx
で説明されています。また、今年8月号の『インターフェース』誌に掲載された解説記事にも、説明が載っているようです(第2章「–新しくなったOSアーキテクチャと変更点– Windows CEにおけるデバイス・ドライバ開発の実際」)。
 http://www.cqpub.co.jp/INTERFACE/contents/2008/JA/200808.htm

僕は『インターフェース』誌を購読していないので、記事の詳細は分かりませんが、参考になる記事ではないかと思います。ちなみに、上のページでは、記事の冒頭2ページが載った PDF をダウンロードできます。

ところで、ユーザ・モード・ドライバだと、アプリケーションからの呼び出しのオーバーヘッドが大きいので、カーネル・モード・ドライバとして動かしたい、と考えることも多いでしょう。なおかつ、そのドライバで、どうしても GUI 表示やネットワーク入出力処理を行いたい、という場合には、別の手段があります。カーネル・モード・ドライバから GUI 表示を行う場合には、CeCallUserProc() というカーネル関数を使います。この関数の説明は、MSDN のリファレンスの
 http://msdn.microsoft.com/en-us/library/aa909021.aspx
にあります。また、Windows CE Base Team Blog の
 http://blogs.msdn.com/ce_base/archive/2006/11/09/CE6-Drivers_3A00_-What-you-need-to-know.aspx
も参考になるでしょう。CeCallUserProc() をカーネル側から呼び出した場合、udevice.exe、つまり User Mode Driver Host を介して、指定した DLL のルーチンが呼び出されます。GUI の表示処理は、その DLL で実装しておくというわけです。

次は、GUI 表示ではなく、ネットワーク入出力です。実は、ネットワーク入出力については、WinSock API をカーネル・モード・ドライバから呼び出すことができるのです。つまり、GUI 表示の場合とは異なり、CeCallUserProc() を使わず、直接 WinSock API を呼び出すことが可能です。これについては、
 http://download.microsoft.com/download/a/0/9/a09e587c-4ff9-4a58-a854-56fe50b862b2/release%20notes.htm
に記載があります(”Do not use k.ws2.dll from kernel mode”)。このページでは、”k.ws2.dll を使わないように”という注意書きがあるのですが、これについては、
 http://msdn.microsoft.com/en-us/library/aa915026.aspx
を併せ読むのがよいでしょう。WinCE での WinSock API の DLL は ws2.dll であり、WinSock API を使うアプリケーションは、ws2.lib をリンクライブラリに加えてビルドします。この ws2.dll のカーネルモジュール用のものが k.ws2.dll というわけです。

上の二番目の Web ページの説明(”Loading DLLs in Kernel Mode or User Mode: Windows CE 5.0 vs. Windows Embedded CE 6.0″)で書かれているように、カーネル・モード・ドライバ、つまりカーネルモジュールがリンクした DLL をロードする際、WinCE 6.0 のカーネルは、リンク指定された DLL の名前の前に ‘k.’ を付けた DLL があれば、そちらを自動的にロードするようです。従って、カーネル・モード・ドライバのビルド設定で、リンクライブラリとして ws2.dll が指定されていた場合、k.ws2.dll がロードされます。しかし、上の一番目の Web ページの注意書きでは、k.ws2.dll を使わないようにと言っていますので、WinSock API を使うカーネル・モード・ドライバでは、リンクライブラリとして、ws2.lib(または k.ws2.lib)を指定せず、カーネルモジュール専用の ws2k.lib をリンクするのがよいでしょう。

なお、WinCE ではなく、WinXP までの WinNT 系カーネルでは、カーネルモジュールからネットワーク入出力を行う場合には、このようなことができず、TDI(Transport Driver Interface) を使う必要があったようです。Vista からは、この点が改善され、WinSock と同様のインタフェースを持つ WSK(WinSock Kernel) が提供されているそうです:
 http://www.exconn.net/Blogs/windows/archive/2006/03/04/7383.aspx
 http://www.atmarkit.co.jp/fwin2k/network/baswinlan017/baswinlan017_03.html
 http://www.themssforum.com/Drivers/Driver-serial-104389/

WinCE 6.0 の ws2k.dll は、この WSK と同じ位置づけだと言えるでしょう。ただし、現状では、ws2k.dll の呼び出しはカーネル側で完結せず、CeCallUserProc() を使ってユーザランド側の DLL を呼び出す場合と同様に、ユーザランド側のモジュールを介して、ユーザランド側から WinSock が呼び出される仕組みになっているようです。これは、上に挙げた
 http://download.microsoft.com/download/a/0/9/a09e587c-4ff9-4a58-a854-56fe50b862b2/release%20notes.htm
で説明されています。実際、WinSock API を使って TCP/IP 通信を行うカーネル・モード・ドライバを作り(※もちろん、そのドライバをビルドする際には、ws2k.lib をリンクします)、カーネルデバッガを接続して動かすと、その様子を見ることができます。たとえば、Ethrnet コントローラのドライバのソースのパケット送出関数にブレークポイントをセットして、そのブレークポイントがヒットした時のコールスタックを見ると、ws2serv.exe をロードした udevice.exe のスレッドからの呼び出しになっています。その状態で、当該デバイスドライバからの WinSock 呼び出しを見ると(※カーネルデバッガの「スレッド」メニューを使えば、executable ごとのスレッド一覧と、各スレッドのコールスタックを表示できます)、ws2k.dll から User Mode Driver Reflector を介して ws2serv.exe を呼び出し、その呼び出しの完了待ちになっている様子が分かります。

将来的には、Vista の WSK と同様、WinCE の ws2k.dll が提供するインタフェースの実装が全てカーネル側に置かれ、それによって、カーネル側からユーザランドを呼び出して戻るオーバーヘッド無しで、WinSock によるネットワーク入出力が可能になるのだと思われます。

Add comment 2008/07/22 koga


Categories

Links

Posts by Authors

Recent Posts

Calendar

2008年7月
    9月 »
 12345
6789101112
13141516171819
20212223242526
2728293031  

Posts by Month

Posts by Category

Meta