PR
杉本 拓也 松井 俊訓 杉本 拓也(すぎもと たくや/左)
松井 俊訓(まつい としのり/右)
株式会社富士通ソフトウェアテクノロジーズ


 Windows VistaなどのデスクトップWindows OSと同様に、Windows Embedded CE 6.0はマルチタスクシステムをサポートしています。「タスク」とは、CPUが処理する1つの仕事の単位です。プロセスやスレッドと同義語で使われる場合もあります。

 プロセスは、アプリケーションの実行単位です。アプリケーションを起動すると、1つのプロセスが生成されます。またスレッドはタスクにCPUを割り当てる単位で、プロセスによって生成されます。プロセスが起動されるとアプリケーションのメインスレッドが1つ生成されます。このメインスレッド内で、アプリケーションエントリポイントであるWinMain()が実行されます。

 アプリケーションやデバイスドライバでは、多くの場合、並行して複数の処理が行われます。これを「マルチタスク」と呼び、複数のタスクを効率よく実行するために、CPUやハードウェアリソース配分をOSが管理することを「タスク管理」と言います。

マルチタスクの司令塔はスケジューラ

 Windows Embedded CE 6.0の大きな特徴は、プリエンプティブマルチタスクOSであることです。「プリエンプティブ」とは「先取りする」というような意味で、1つのCPUで複数のタスク(マルチタスク)を処理する時に、各タスクにCPUを割り当てる時間をOSが管理して優先度の高いタスクに切り替える方式のことです。

 1つのCPUは同時に複数のスレッド(タスク)の処理を行うことはできません。そのため、CPUの時間を細かく分割して、OSがスレッドを切り替えながらCPUの実行時間を割り当てます。この割り当てを行うのが、OSのカーネルにあるスケジューラです。また、このときスレッドの切り替えのタイミングを制御するのは、ハードウェアタイマ割り込みです。

 つまり、プロセスがスレッドのスケジューリングを行うのではなく、カーネルのスケジューラが、システム上で起動されているすべてのスレッドをスケジューリングするのです。では、スケジューリングの仕組みを見てみましょう。

256段階でスレッドに優先順位をつける

 OS上では、デバイスドライバやアプリケーションによって生成された複数のスレッドが常に実行されていますが、すべてのスレッドに対して均一のCPU 時間を割り当てる「ラウンドロビン方式」を採用すると、不都合が生じます。たとえば、アプリケーションの処理中にあるデバイスから割り込みが発生した場合でも、CPU の実行時間がすぐには割り当てられないために割り込み処理ができないかもしれません。

 Windows Embedded CE 6.0では、各スレッドに対して優先度を設定する「優先度スケジューリング方式」を採用しています。優先度のレベルは256段階(一番高い優先度が0、一番低い優先度が255)で設定できます。優先度が異なる複数スレッドが実行状態のときは、優先順位が高いスレッドが待ち状態にならない限り、それより低い優先順位のスレッドは実行できません。

 同じ優先順位のスレッドが実行状態のときは、ラウンドロビン方式で実行されます。スレッドを生成した直後のデフォルトの優先順位は、251に設定されています。また、デバイスドライバやアプリケーションなどのスレッドの優先順位は、表1のように定義されています。これらの優先度の設定や取得にはAPIが用意されています。

表1●スレッドの優先順位。数値が小さいほど優先度が高い
優先順位 コンポーネント
0 ~ 96 リアルタイム性を必要とするドライバ
97 ~ 152 通常のドライバ
153 ~ 247 リアルタイム性を必要としないドライバ
248 ~ 255 上記以外のリアルタイム性を必要としないアプリケーションやサービス

 また、優先順位が高いスレッドが待ち状態にならない限り、それより低い優先順位のスレッドは実行されません。スレッドが待ち状態になることを「ブロック状態になる」と言います。アクティブなスレッドをブロック状態にして他のスレッドに制御を渡したい場合は、ブロッキングAPIを使用します。

100ミリ秒ずつ割り当てるスレッドクァンタム

 スレッドが同じ優先度の場合は、ラウンドロビン方式によって一定の時間が割り当てられます。この一定の時間間隔をスレッドクァンタムと呼びます。スレッドクァンタムのデフォルト値は100ミリ秒です。つまり、同じ優先順位の3つのスレッドが実行されていたら、100ミリ秒ごとにCPUの実行権限がそれぞれのスレッドに交互に与えられます。

 デフォルトのスレッドクァンタム値を変更したい場合は、OALの初期化関数であるOEMInit内で、カーネルのグローバル変数dwDefaultThreadQuantumの値を変更します。次のコードは、75ミリ秒に変更する例です。


 extern DWORD dwDefaultThreadQuantum;
 void OEMInit()
 {
 ...
   dwDefaultThreadQuantum = 75; // 75ミリ秒
 ...
 }

 また、アプリケーションやデバイスドライバ内で動的にスレッドクァンタムを変更したり取得するときは、APIを利用します。