特集
Windows 9x or Windows 2000?

コラム:Windows 3.xのマルチタスク システム

デジタルアドバンテージ
2001/07/28

シングル タスクのMS-DOSの上で動くウィンドウ システムとして設計されたWindowsでは、ノン プリエンプティブなマルチタスク(non-preemptive multitasking)または協調型マルチタスク(cooperative multitasking)と呼ばれるマルチタスク方式が採用された。当初のWindowsシステムは、言葉は悪いが、GUIプログラムのラウンチャ(launcher、ほかのプログラムを起動するためのプログラム)に過ぎず(MS-DOSは、CUIプログラムのラウンチャ)、本格的な(プリエンプティブな)マルチタスキング オペレーティング システム環境を目指していたわけではない。平均的なユーザーの搭載メモリ容量やCPU性能、および開発工数を抑えるという観点などから、MS-DOS上で稼動する、ノン プリエンプティブなマルチタスク ウィンドウ システムとして開発されたのである。MS-DOSをベースに用いることにより、ファイル入出力などを全面的にMS-DOSにまかせることができる(ただし、同時に複数のアプリケーション プログラムからMS-DOSのシステム コールを呼び出さないように制御する必要がある)。

 別稿ですでに述べたとおり、「preemptive」は「先取権」という意味で、ノン プリエンプティブなマルチタスクでは、現在実行中のアプリケーションが、自主的に制御を解放するまで、Windowsシステムを含む別のアプリケーションは実行されない。したがっていつまでも制御を解放しないアプリケーションが存在すると、他のアプリケーションはもとより、Windowsシステムですら動くことができないのである。マルチタスク システムとしては、何とも心許ない仕様だが、代わりにマルチタスク システムを実現するための機構は非常に単純で、タスク スイッチなどにかかわるオーバーヘッドも少ないという長所がある。

 なおWindows 3.xシステムでは、アプリケーションごとに独立したメモリ空間は提供されていなかった。そこでWindows 3.1環境で実行されるWin16アプリケーションは、メモリ空間など独自のリソースを持つWindows 9xやWindows 2000のプロセス(process)とは区別し、ここではタスク(task)と呼ぶことにする。

メッセージ システム = マルチタスク システム

 Windows環境では、「キーボードが押された」、「マウスが移動された」、「アクティブなウィンドウが切り替えられた」など、ユーザーによる操作や、Windowsシステムによる処理の結果が、Windowsメッセージとして各アプリケーションに通知されるようになっている。Windows 3.xのマルチタスク システムは、このメッセージ システムを素直にインプリメント(実装)したものだといってよい。

Windows 3.xのメッセージ 処理
Windows 3.xシステムでは、マウスやキーボード操作、その他デバイスからの応答やWindows システムの処理、アプリケーションから別のアプリケーションへの通信など、さまざまなイベント(出来事)がメッセージとしてアプリケーションに通知される。これらのメッセージは、Windows 3.xシステム内部にあるシステム メッセージ キューに発生順にキューイングされ、メッセージの宛先であるアプリケーションに渡される。

 このようにWindows 3.xでは、あらゆるイベント(出来事)がWindowsメッセージと呼ばれる構造化データとしてアプリケーションに通知される。そしてメッセージを受け取ったアプリケーションは、メッセージの内容に応じた処理を逐一行う。たとえば、特定のメニューが選択されたことを知らせるメッセージを受け取ったら、そのメニュー項目に対応する処理を実行する。アプリケーションがワードプロセッサで、何らかのキーが押されたなら、そのキーに対応する文字をウィンドウ上に表示する、といった具合である。そしてメッセージ処理が完了したら、アプリケーションは制御を解放し、次のアプリケーションにメッセージが送られる。

 アプリケーション プログラムの視点からこの様子を図にすると次のようになる。

Windows 3.xのマルチタスク
Windows 3.x環境で実行されるWin16アプリケーションは、メッセージ ループと呼ばれるプログラム ループの中で、システムから自分宛のメッセージを取り出すためのGetMessage APIを呼び出す。GetMessage呼び出しによって制御を得たWindows 3.xシステムは、次のメッセージの宛先であるアプリケーションのGetMessageにメッセージの情報を戻り値として返す。メッセージを受け取ったアプリケーション(GetMessage呼び出しがリターンしたアプリケーション)は、受け取ったメッセージを処理し、再度GetMessageを呼び出す。このように、GetMessage関数呼び出しと、それへのリターンを繰り返し行うことで、マルチタスクを実現しているのである。

 Windows 3.x上で動作するWin16アプリケーションは、プログラムの内部にメッセージ ループと呼ばれるループを必ず含んでいる。具体的には次のようなものだ。

while (GetMessage(…))
{
  受け取ったメッセージの処理
}
アプリケーションの終了

 名前が示すとおり、GetMessageは、自分宛のメッセージをWindows 3.xシステムから取り出すAPIである。キーボードが押された、マウスが移動したなど、自分が処理すべきイベントが発生したときには、それらがメッセージとして与えられるのだが、それをシステムから取り出してくるAPIがGetMessageである。GetMessageによってメッセージを取り出したアプリケーションは、メッセージの内容に従って適当な処理を行い、処理を完了すると、whileループによって再度GetMessageを呼び出す。このメッセージ ループによるGetMessage呼び出しは、アプリケーションが実行されている間じゅう繰り返される(GetMessage APIはnull以外の値を戻す)。

 そしてアプリケーションが終了させられると([ファイル]−[終了]メニューが実行されるなど)、初めてGetMessageが戻り値としてnullを戻し、whileループを脱出し、アプリケーションの処理を完了する。つまりWin16アプリケーションにとってのメッセージ ループは、心臓の鼓動のようなものなのだ。

 Win16アプリケーションがGetMessage APIを呼び出すと、制御はWindows 3.xシステムに移る。するとWindows 3.xシステムは、メッセージ キューを調べて、次に処理すべきメッセージの宛先となるアプリケーションを特定する。そしてWindows 3.xは、そのアプリケーションのGetMessageに対して、メッセージ データとともにリターンするのである。こうしてアプリケーションに制御が移ると(メッセージ ループのGetMessage呼び出しから戻ると)、アプリケーションはメッセージを処理し、再度GetMessageを呼び出す。すると再度Windows 3.xシステムに制御が移り……、というふうにして、マルチタスクが実現されるわけだ。

ノンプリエンプティブなマルチタスクの長所

 このようなWindows 3.xのノンプリエンプティブなマルチタスクの長所は、マルチタスク システムとしてのオーバーヘッドが非常に少ないということだ。上の図からも分かるとおり、あるWin16アプリケーションが制御を得ているときには、他のWin16アプリケーションはもとより、Windows 3.xシステムでさえもまったく動いていない。つまりWin16アプリケーションは、システム パワーをほぼ独り占めにできるわけだ。

 さらに重要なことは、現在制御を得ているWin16アプリケーションが何らかのシステム サービスを受けるために、Windows 3.xのシステム サービス(API)を呼び出したときは、そのAPIの処理を完了してアプリケーションに制御を戻すまでは、誰もWindows 3.xシステムを呼び出すことはないということだ。Windows 2000のようなプリエンプティブなマルチタスク システムでは、たとえシステム サービスの実行中でも、制御が他のアプリケーションに与えられ、そのアプリケーションがシステム サービスを呼び出す可能性がある。このような処理は一般に再入(リエントラント:re-entrant)と呼ばれ、再入可能性のあるシステムでは、それらを正しく処理できるようにコードを設計しなければならない。具体的には、グローバル変数などは使わず、呼び出し側のスタックなどを使って変数を格納するようにする(これによりプログラム コードでは、現在の呼び出し元に応じた変数の処理が可能になる)。Windows 3.xシステムは、再入の可能性がまったくない。したがってWindows 3.xシステムは、面倒な再入対策を施す必要がないのだ。

ノンプリエンプティブなマルチタスクの短所

 ノンプリエンプティブなマルチタスクの短所は説明するまでもないだろう。図からも分かるとおり、マルチタスク システムを実現するうえにおいて、Windows 3.xのそれは、何ら強制力を持っていない。このためたとえば、あるWin16アプリケーションが制御を得たまま、バグなどで無限ループに入ってしまうと、システム全体がフリーズしてしまう(不都合のあるアプリケーションを強制的に終了させるなどして、制御を取り戻す方法がない)。

 またノンプリエンプティブなマルチタスクでは、あるアプリケーションが制御を得てから、制御を解放するまでの時間がアプリケーションまかせになっている。Win16アプリケーションの設計規約では、「ごく短い時間」で1つのメッセージ処理を終えるように指導しており、時間がかかる処理を行う必要があるときには、タイマなどを設定して(これにより、一定時間ごとにタイマ メッセージを受け取れるようになる)、当初のメッセージ処理はすぐに終了し、それ以後に受け取るタイマ メッセージで処理を何回かに分けて行うなどの工夫をすることになっていた。

 つまりWindows 3.1のマルチタスクは、制御を得た各アプリケーションが、ごく短い時間のうちにメッセージ処理を終え、制御をすぐに戻すという善意の上に成り立っているのだ。しかしアプリケーションがいくらこうした工夫をしたとしても、ネットワークの接続先が正常に機能していなかったことによるタイムアウト待ちや、想定外のパラメータが与えられた場合の処理など(たとえば、通常は10回程度のループで済む処理に、何らかの手違いで莫大な数のループ カウンタが与えられてしまうなど)、アプリケーション側ではどうしようもない場合もある。


 INDEX
  [特集]Windows 9x or Windows 2000?
     1.イントロダクション
     2.Windows 9xカーネルの概要
      コラム:Windows歴史、メモリの歴史 (1)
      コラム:Windows歴史、メモリの歴史 (2)
     3.Windows 2000カーネルの概要 (1)
     4.Windows 2000カーネルの概要 (2)
     5.プロセス管理の概要
   コラム:Windows 3.xのマルチタスク システム
     6.Windows 9xのプロセス管理メカニズム (1)
     7.Windows 9xのプロセス管理メカニズム (2)
     8.Windows 2000のプロセス管理メカニズム (1)
     9.Windows 2000のプロセス管理メカニズム (2)
 
 特集
 


Windows Server Insider フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Windows Server Insider 記事ランキング

本日 月間