Archive for 2015/09

OS の起動時間短縮(2/2)~ビルトインのデバイスドライバを遅延ロード

■はじめに
昨年9月のエントリで、「今回と次回は、WEC の起動時間を短縮する方策を、二つ紹介します。」と述べて一つめの方策を書いた後、一年近く時間が空いてしまいましたが、二つめの方策を書きます。

前回紹介した方策では、RAM レジストリを永続化した構成において、RAM レジストリの初期化時間を短縮する、というものでした。今回紹介するのは、ビルトインのデバイスドライバのうち、起動直後ないし起動中に動作していることが必須ではないものを、OS 起動中にはロードされないようにする(必要になった時点で後からロードする、つまり遅延ロードする)、という方策です。

この方策により、WEC が起動して初期画面(標準シェルを組み込んだ場合は、デスクトップ画面)が表示されるまでの時間を若干短縮することが可能です。短縮される時間は、遅延ロードするドライバ群の初期化処理に要する時間の合計です。従って、初期化処理に要する時間が長いドライバがあれば、遅延ロードさせる対象の候補となります。

注意:WEC 2013 には、標準シェル(explorer.exe)は付属しません。代わりに、最少機能のミニシェルを使う必要があります。

もちろん、遅延ロードさせるドライバは、WEC の起動直後ないし起動中に動作していなくても構わないものに限られます。たとえば、ディスプレイドライバは、WEC のウィンドウシステムである GWES が必要としますので、遅延ロードさせるわけにはいきません。また、LCD ディスプレイを接続した端末であれば、LCD バックライトのドライバも、WEC の起動中に動作を開始しなければいけません(そうでないと、LCD の画面が真っ暗で、初期画面が表示されませんからね)。逆に、USB や SD のホストコントローラのドライバは、遅延ロード対象の候補になるでしょう。

USB や SD の、対向デバイスごとのドライバ、つまりクラスドライバは、対向デバイスが接続された時点で初めてロードされる Plug & Play ドライバですが、それらをロードするホストドライバを遅延ロードさせても、問題ないケースは多いでしょう。他の候補としては、バッテリードライバも挙げられます。これら、ビルトインのデバイスドライバ(※USB や SD のクラスドライバのような Plug & Play ドライバではなく、OS の起動時にロードされるドライバ)のうち、遅延ロード可能なものを洗い出して遅延ロードさせることにより、WEC が起動して初期画面が表示されるまでの時間を、数10[ms]~数100[ms]、場合によっては数秒程度短縮できるかも知れません。

ビルトインのデバイスドライバを遅延ロードさせることによる、WEC の初期画面が表示されるまでの時間の短縮は、OS のコンフィグレーションや、遅延ロードさせるドライバが初期化時に行う処理内容、及びドライバが制御するハードウェアに依存します。つまり、どれくらいの時間が短縮されるかは、ケースバイケースです。そのため、ここでは具体値には触れず、上記の定性的な説明に留めます。

なお、今回の説明に通じる内容は、WEC 2013 のディベロッパーガイドでも触れられています。興味のある方は、MSDN にある、次のページもご覧になってみて下さい。

 Optimize Driver Load Time (Compact 2013)
 https://msdn.microsoft.com/en-us/library/dn194541.aspx

■ビルトインのデバイスドライバ
さて、遅延ロードの手順について説明する前に、ビルトインのデバイスドライバとは何を指すのかを、明らかにしておきましょう。上で、ビルトインのデバイスドライバを「OS の起動時にロードされるドライバ」と書きましたが、OS の起動時にロードされるかどうかは、レジストリ設定で決まります。具体的には、ドライバのレジストリキーが
 [HKEY_LOCAL_MACHINE\Drivers\BuiltIn]
の下に設定されていると、OS の起動時にロードされるのです。このことは、WEC 2013 のディベロッパーガイドの次のページで説明されています:

 Device Manager (Compact 2013)
 https://msdn.microsoft.com/en-us/library/jj659831.aspx

上のページの中ほどにある、”Registry Settings” という項をご覧ください。ここに、デバイスドライバのレジストリ項目の説明もあります。

■デバイスドライバのロード防止設定
上述したように、レジストリキーを [HKEY_LOCAL_MACHINE\Drivers\BuiltIn] の下に配置したデバイスドライバは、OS の起動時に自動的にロードされます。この設定を変え、”BuiltIn” ではなく、他のキーの下にレジストリキーを配置すると、OS の起動時にはロードされません。そのような場合は、ActivateDevice[Ex]() を使って明示的にロードしない限り、ロードされることはありません。

たとえば、バッテリドライバのレジストリキーは
 [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\Battery]
ですが、これを変更し、
 [HKEY_LOCAL_MACHINE\Drivers\AddOn\Battery]
にすると、起動時にロードされなくなります。つまり、ビルトインのデバイスドライバを OS の起動時に自動ロードされないようにするいは、そのレジストリキーを変更し、[HKEY_LOCAL_MACHINE\Drivers\BuiltIn] の下ではない階層に移せばよいのです。

ただし、バッテリドライバのように、WEC 標準のデバイスドライバのレジストリキーを変更してしまうと、不都合を生む場合があります。そのドライバに対するレジストリ設定が、複数の .reg ファイルに分割して記述されている場合です。実際、端末が違っても変わらない共通的な設定は WEC 付属の共通設定を記述した .reg ファイル(%_WINCEROOT%/public/common/oak/files/common.reg)に記述し、端末ごとに変わる設定を、OS Design プロジェクトの .reg ファイルや、あるいは、BSP の .reg ファイルに記述する、ということは珍しくありません。その際、WEC 標準で決まっているレジストリキーを変更してしまうと、不整合が起きてしまう危険があります(※当該ドライバに対する、全ての .reg ファイルに記載された設定を変更できず、一部は BuiltIn 配下のままになってしまう、というケースが考えられます)。その危険を回避するには、レジストリキーは標準設定のまま(※つまり、ビルトインのデバイスドライバのレジストリキーは、 BuiltIn の下に配置する設定のまま)で、自動ロードされるかどうかだけを変更することが必要です。そして、そのためのレジストリ項目があるのです。

デバイスドライバの自動ロードは、Flags というレジストリ項目の設定値で防止できます。上で紹介したページの、”Registry Settings” という項にある表を見て下さい。表の4行目にある、DEVFLAGS_NOLOAD というフラグが、そうです。デバイスドライバのレジストリ設定で、Flags の値が DEVFLAGS_NOLOAD (0×00000004) を含むと、そのドライバはロードされません。つまり、BuiltIn の下にレジストリキーが配置されていても、OS の起動時に自動ロードされません。ただし、OS の起動時に限らず、ActivateDevice[Ex]() を使ってもロードできません。

■ビルトインのデバイスドライバの遅延ロード手順
デバイスドライバのレジストリ設定において、Flags に DEVFLAGS_NOLOAD (0×00000004) を含む値を設定することにより、ビルトインのデバイスドライバであっても、OS の起動時に自動的にロードされるのを防ぐことができるのは分かりました。しかし、そのままでは、ActivateDevice[Ex]() を使ってもロードできず、ロードできないままになってしまいます。では、どうすればよいのでしょうか?

解決方法は、簡単です。ドライバをロードする時に、レジストリ設定を一時的に変更すればよいのです。つまり、ActivateDevice[Ex]() を呼び出す前に、レジストリの Flags の値から DEVFLAGS_NOLOAD フラグを取り除き、ActivateDevice[Ex]() の呼び出しが終わったら、再度 DEVFLAGS_NOLOAD フラグを加えればよいのです。

つまり、ビルトインのデバイスドライバを遅延ロードするために必要な手順は、次の通りです:

1.) OS Design のレジストリ設定ファイル(OSDesign.reg)において、遅延ロード対象のデバイスドライバの Flags の値を、DEVFLAGS_NOLOAD (0×00000004) を含むものに設定するよう記述を追加する(※common.reg などで Flags が設定されていれば、OSDesign.reg で上書き設定する)。

2.) 必要になった時点で、次の処理を実行する:
 2-1.) RegSetValueEx() を使って、そのドライバのレジストリの Flags の値を変更する(DEVFLAGS_NOLOAD を含まない値に変更する)。
 2-2.) そのドライバのレジストリキーを指定して ActivateDevice[Ex]() を呼び出す。
 2-3.) 再び RegSetValueEx() を使って、Flags の値を変更前のものに戻す。

上の手順のうち、(2) は、WEC の起動後に自動実行されるようにするのが簡単でしょう。遅延ロード対象のデバイスドライバ群に対して (2) を実行するアプリケーションを作り、そのアプリケーションを、/Windows/Startup/ ディレクトリに配置すれば、WEC が起動して、標準シェルのデスクトップ画面が表示された直後に、遅延ロード対象のデバイスドライバ群がロードされて動作開始します。この時、画面表示を見ているユーザにとっては、それらのデバイスドライバが起動時に自動ロードされる場合よりも起動時間が短縮されたように見えるでしょう。

■おまけ
/Windows/Startup/ ディレクトリに配置されたアプリケーションを自動起動する処理は、標準シェルのソースコード(※WEC 2013 には付属しません)でいうと、
 %_WINCEROOT%/public/shell/oak/hpc/explorer/main/explorer.cpp
で実装されている ProcessStartupFolder() で行われます。この関数が、SHGetSpecialFolderPath() で Startup ディレクトリのパス(つまり、/Windows/Startup/)を取得して、その中に入っている実行ファイル全てに対して順次 ShellExecuteEx() を呼び出す、という仕組みです。ProcessStartupFolder() は、DoStartupTasks() を介して、標準シェル(explorer.exe)のメインルーチンである WinMain() から呼び出されます。

explorer.exe の WinMain() では、標準シェルの初期化動作を行った後、CDesktopWnd クラスのインスタンスを生成して Create() を呼び出し、デスクトップ画面を表示します。さらに、CreateTaskBar() を実行するスレッドを生成・始動して、タスクバーを表示します。それらが済んだ後で、DoStartupTasks() を呼び出すようになっています。従って、遅延ロード対象のデバイスドライバをロードする処理は、/Windows/Startup/ に配置したアプリケーションで行うようにしておけば、標準シェルの初期画面が表示された後で遅延ロードが実行されるので、遅延ロードのぶん、初期画面が表示されるまでの時間が短縮される、というわけです。

標準シェルを使わず、カスタムシェルを使う場合(WEC 2013 のミニシェルを使う場合など)も、同じような方策で対応できるでしょう。WEC 7 をお持ちで、興味のある方は、explorer.cpp の内容を眺めてみると、面白いかも知れません。

Add comment 2015/09/16 koga


Categories

Links

Posts by Authors

Recent Posts

Calendar

2015年9月
« 2月    
 12345
6789101112
13141516171819
20212223242526
27282930  

Posts by Month

Posts by Category

Meta