Archive for 2014/09

OS の起動時間短縮(1/2)~RAM レジストリの初期化時間を短縮する

■はじめに
今回と次回は、WEC の起動時間を短縮する方策を、二つ紹介します。WEC 2013 では、Snapshot Boot が導入され、適切に実装・チューニングすれば、OS の起動時間を数秒以内に抑えることも可能になりました。しかし、WEC 2013 よりも前の版では、Snapshot Boot を行うことができません。そこで、WEC 7 でも対応可能な起動時間短縮の方策を紹介してみることにしました。なお、Snapshot Boot については、MSDN の次のページをご覧ください。

 Snapshot Boot Development (Compact 2013)
 http://msdn.microsoft.com/en-us/library/dn169259.aspx

日本国内の Windows Embedded MVP 有志が主催して不定期に開催している、「Windows Embedded Community Day」の第1回で僕が担当したプレゼンでも、Snapshot Boot について簡単に紹介しています:

 組み込みでもマルチコア。WEC2013 での対応
 http://www.slideshare.net/ShinyaKoga/2013-0719wemvp-kansai

 Windows Embedded Community Day 第 1 回 – 真夏の組み込み Windows 技術セミナー2013
 http://msdn.microsoft.com/ja-jp/dn521045

■RAM レジストリの初期化
2011/08/02 に書いたエントリ(「レジストリの永続化~RAM-Based の場合」)では、レジストリの永続化対応を行う場合、Hive-Based のレジストリよりも、RAM-Based のレジストリの方が、より堅牢だと述べました。しかし、RAM-Based のレジストリには、起動時間が長くなるという短所があります。これは、レジストリの初期化に要する時間が、RAM-Based のレジストリの方が長いことが要因のようです。

Hive-Based のレジストリは、レジストリの Hive ファイルを filesys.dll が memory mapped file としてオープンして内容にアクセスします。つまり、Hive ファイルの内容は、filesys.dll が RAM 上に構築するレジストリ(一種のオンメモリデータベース)と同じではないかと思われます。従って、OS の起動時にレジストリを初期化する動作というのは、OS イメージに収録されている Hive ファイルや、内容更新されて永続化記憶域(ストレージ)に配置された Hive ファイルを、単に memory mapped file としてオープンするだけなのでしょう。

一方、RAM-Based のレジストリは、filesys.dll が RAM 上に構築するレジストリとは内容(フォーマット)が異なるようです。このため、OS の起動時にレジストリを初期化する際、ストレージからレジストリデータを読み出し、その内容を解析して RAM 上にレジストリを構築する、という処理が必要になります。このため、レジストリの初期化に要する時間が長くなるのです。この時間は、レジストリ全体のサイズに依存しますから、一般的には、OS イメージに組み込む機能が多いほど、つまり、OS Design のカタログ項目の選択が多いほど長くなります。10秒以上に達することがあることを確認していますし、永続化した場合は、レジストリの初期化時間の合計で20秒を超える場合もあります。

■永続化された場合の RAM レジストリの初期化
上で、「永続化した場合は」と書きました。そうです。RAM レジストリを永続化した場合、永続化しない場合よりも、レジストリの初期化に要する時間が長くなるのです。これについては、2011/08/02 に書いたエントリでも紹介した、WinCE 6.0 のリファレンスにある、次のページの説明にヒントがあります。

 File System Initialization of RAM-based Registry (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-US/library/ee490364(v=winembedded.60).aspx

このページには、RAM-based レジストリの初期化の流れが説明されているのですが、1から8まであるステップのうち、5と6は、次の通りです:

  1. Filesys.dll initializes the registry by checking for a registry in RAM.
    If Filesys.dll does not find a registry in RAM, it restores the registry from ROM. Filesys.dll loads \Windows\Default.fdf, which was created using Makeimg.exe.
  2. Filesys.dll checks for the ReadRegistryFromOEM OEM adaptation layer (OAL) function. If Filesys.dll does not find ReadRegistryFromOEM, it keeps the registry restored from ROM.

つまり、OS イメージ(ROM イメージ)に収録されている Default.fdf というファイル内容をロードして初期化した後、OAL の ReadRegistryFromOEM() を(それが実装されていれば)呼び出し、ストレージに保存されたレジストリデータをロードして初期化し直す、というわけです。つまり、RAM-Based のレジストリを永続化した場合、レジストリの初期化処理が実質二回行われるのです。

■永続化された RAM レジストリの初期化時間を短縮
RAM-Based のレジストリを永続化した場合に、永続化しない場合よりもレジストリの初期化に要する時間が長くなるのは、レジストリの初期化処理が二回行われるからなのです。これを、一回にすることができれば、そのぶん起動時間を短縮できることになります。何かよい方法はないでしょうか?

あります。それは、2012/01/13 に書いたエントリ(「OS の部分アップデート方策~その2(2/2)」)で述べた、ROM イメージを複数に分割することです。このエントリで、ROM イメージを複数の region に分割した場合、同じ名前(パス)のファイルに対して、region 間で shadowing が作用すると書きました(※「Region ごとの部分アップデート」という項をご覧ください)。この shadowing を利用するのです。

方策は、次の通りです:

1.) OSDesign.reg において、最小限の内容(たとえば、[HKLM\init] キーだけ)が有効になるように設定して OS イメージをビルドして、生成された Default.fdf ファイルを採取する。

2.) (1) で採取した Default.fdf ファイルのみを収録する ROM イメージの region を作成する。
 これは、config.bib によるメモリマップの設定と、ブートローダによる複数 region のロード、および、OAL による複数 region の連結処理(OEMRomChain のリスト構築処理)が必要です。

3.) 通常のビルド時は、(1) の設定を解除して OS イメージをビルドする。
 この結果、本来のレジストリ設定内容は、OS 本体を収録した region(NK region)に Default.fdf という内容で収録され、(1) で作成した(最少サイズの)Default.fdf は、それ専用の region に収録されます。

4.) OAL における OEMRomChain のリスト構築処理を実行する前に、ストレージ上にレジストリデータが存在するかどうかをチェックし、存在しない場合は、OEMRomChain のリスト構築処理において、Default.fdf のみを収録した region を無視する。ストレージ上にレジストリデータが存在する場合は、Default.fdf のみを収録した region を、OEMRomChain のリストにおいて、OS 本体を収録した region よりも前に配置する。

この方策により、ストレージ上にレジストリデータが存在する場合は、(1) で作成した最小限の Default.fdf ファイルの内容で一回目のレジストリ初期化が実行され、その時間を最小限(相対的に 0)にできます。その後、ストレージに保存されたレジストリデータがロードされ、レジストリが初期化される、というわけです。

ストレージ上にレジストリデータが存在しない場合は、(1) で作成した最小限の Default.fdf ファイルを収録した region は OEMRomChain のリストには挿入されませんから、shadowing が行われず、OS 本体を収録した region(NK region)に収録された、通常の Default.fdf がロードされてレジストリが初期化されます。従って、レジストリを一度もストレージに保存していない場合や、ストレージに保存したレジストリデータを無効化した場合にも、問題なく動作します。上記方策を適用しない場合の動作と比べて、起動時間が短縮される以外は、見た目の動作は変わりません。

Add comment 2014/09/23 koga

WEC のネットワークフィルタドライバ(NDIS フィルタ)

今回は、ネットワークドライバに対するフィルタドライバについて述べます。既存のドライバに対してフィルタドライバを attach することにより、ドライバの動作診断に役立てることができます。あるいは、どのように呼び出されるのかという視点からドライバの振る舞いを解析し、既存のドライバを改修する際に役立てたり、また、サンプルドライバの動作を解析して新規にドライバを実装する際の参考にする、といった利用もできるでしょう。

■はじめに~WEC 7 での NDIS 6.0 の導入
NDIS フィルタドライバは、WEC/WinCE で NDIS 6.0 に対応した版、つまり WEC 7 で導入されました。リファレンスにもサンプルコードにも、NDIS フィルタドライバが登場するのは WEC 7 からで、WinCE 6.0 にはありません。WEC 7 と WEC 2013 のリファレンスの、NDIS フィルタドライバの説明は、それぞれ次のページです:

 NDIS Functions for Filter Drivers (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg158504(v=winembedded.70).aspx

 NDIS Filter Driver Reference (Compact 2013)
 http://msdn.microsoft.com/en-us/library/gg159366.aspx

NDIS 5.x だった WinCE 6.0 では、NDIS フィルタドライバに相当する機能は、NDIS 中間ドライバ(NDIS intermediate driver)として提供されていました。興味のある方は、WinCE 6.0 のリファレンスの次のページをご覧ください。

 Intermediate Drivers (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-us/library/ee483408(v=winembedded.60).aspx

NDIS の 5.x と 6.0 の違いについては、次のページが参考になるでしょう。

 Port Miniport Drivers from NDIS 5.x to 6.0 (Compact 7)
 http://msdn.microsoft.com/en-us/library/jj838857(v=winembedded.70).aspx

 Port Miniport Drivers from NDIS 5.x to 6.0 (Compact 2013)
 http://msdn.microsoft.com/en-us/library/jj838857.aspx

 Introduction to NDIS 6.0
 http://msdn.microsoft.com/en-us/library/windows/hardware/ff556026%28v=vs.85%29.aspx

■NDIS フィルタドライバのサンプルコード
NDIS フィルタドライバのサンプルコードは、WEC 7 も WEC 2013 も、次のディレクトリに収録されています:

 %_WINCEROOT%/public/common/oak/drivers/netsamp/lwfilter/

このサンプルコード(lwfilter)は、WEC 2013 のサンプルコードページにも WEC 7 のサンプルコードページにも、説明が載っていません。そのため、見落としていた方もいらっしゃるかも知れません。もし興味がわいたら、この機会に、ご覧になってみて下さい。lwfilter は、Windows Vista/7 用の NDIS 6.0 フィルタドライバのサンプルコードがベースになっているようで、同じ名前の Win7 用のサンプルコードが、次のページで提供されています:

 NDIS 6.0 Filter Driver
 https://code.msdn.microsoft.com/windowshardware/NDISLWFSYS-Sample-NDIS-60-42b76875

上述した、NDIS 5.x の NDIS 中間ドライバのサンプルコードは、WEC 7 以降でも提供されており、次のディレクトリに収録されています:

 %_WINCEROOT%/public/common/oak/drivers/netsamp/passthru/

NDIS 中間ドライバが WEC 7 以降も使えるのは、NDIS 5.x のサポートが含まれているからです。NDIS 5.x 関連のドライバインタフェースは、リファレンスの次のページで説明されています:

 NDIS 5.x Legacy Reference (Compact 7)
 http://msdn.microsoft.com/en-us/library/gg158312(v=winembedded.70).aspx

 NDIS 5.x Legacy Reference (Compact 2013)
 http://msdn.microsoft.com/en-us/library/gg158312.aspx

ちなみに、WEC 7 と WEC 2013 のネットワーク関連のサンプルコードは、次のページで紹介されています。興味のある方は、こちらもご覧になってみて下さい。

 Networking Code Samples (Compact 7)
 http://msdn.microsoft.com/en-us/library/hh802406(v=winembedded.70).aspx

 Networking Code Samples (Compact 2013)
 http://msdn.microsoft.com/en-us/library/hh802406.aspx

■フィルタドライバの効用
最後に、フィルタドライバの効用について述べます。冒頭で、既存のドライバの動作診断や、振る舞いの解析に役立てることができると書きましたが、その他に、既存のドライバに対する機能追加、という効用があります。NDIS は、フィルタドライバが attach されたドライバを直接呼び出さず、フィルタドライバを経由して間接的に呼び出します。従って、フィルタドライバは、既存のドライバに対する NDIS からの呼び出しをフックして追加の処理を実行できるのです。つまり、既存のドライバの実装を変更せずに、フィルタドライバによって機能追加を行うことができる、というわけです。

フィルタドライバにより機能追加を行うという方策は、機能追加したい対象のドライバが複数存在する場合に、より効果的です。たとえば、NDIS フィルタドライバによってドライバに機能拡張したいデバイスが、複数のネットワークインタフェースを持つ場合のことを考えてみて下さい。有線 LAN のインタフェースと無線 LAN のインタフェースを両方持つデバイスは、近年珍しくありません。それら複数のネットワークインタフェースのドライバに対して、同じ機能追加を行う場合、個々のドライバの実装を変更して機能追加するよりも、NDIS フィルタドライバを使って機能追加する方が、実装変更箇所が一つで済みますから、より低コストで実現できるでしょう。既存のドライバの実装を変更しなくて済むというのは、既存のドライバのソースコードを入手できず、自分で改変できない場合にも役立つ点です。

NDIS は、ネットワークインタフェースのハードウェアを抽象化したレイヤですから、そのレイヤのインタフェースに対するフィルタドライバは、異なるネットワークインタフェースのドライバに対して共通に適用できます。機能追加の例として、「ネットワーク通信が行われている間は、WEC の Power Manager の activity timer のイベントを発火させ、自動サスペンドしないようにする」という機能があります。この機能は、個々のネットワークインタフェースのドライバの実装を改変して実装するよりは、NDIS フィルタドライバで実装し、フィルタドライバを各ネットワークインタフェースのドライバに attach する方がシンプルで良いでしょう。これは、あくまでも一つの例に過ぎませんが、フィルタドライバの役立て方として参考になれば幸いです。

Add comment 2014/09/07 koga


Categories

Links

Posts by Authors

Recent Posts

Calendar

2014年9月
« 8月   2月 »
 123456
78910111213
14151617181920
21222324252627
282930  

Posts by Month

Posts by Category

Meta