今回は,以下の図の CPU 周りのもっとも基本的な,プロセス管理について説明する.
プロセス(process) は,スレッド(thread), タスク(task)と同じ概念であるが、本講義では、プロセスという. スレッドは、特に軽量プロセス(light weight process) ともいい, 通常のプロセス(重量プロセス(heavy weight process)という) と 比較された表現を使うこともある.
プロセスは,例えば,Linux では,ps というコマンドで調べることができる.
マルチタスキング OS では,このように通常多くのプロセスが起動されている. 単一プロセッサでは,このプロセスに一様に処理を割り当てるために, 時分割(time slicing) でプロセスを切り替えている.この切り替えを コンテキストスイッチ(context switch) という.プロセスの処理の 様子は,以下のようなタイムチャート(time chart) で示す.
点線の矢印は,時分割で各プロセスを切り替えている様子を示している. 実際の時分割の間隔は,図では表現できないくらい微小な時間(数マイクロ秒から, 数ミリ秒位) である.そのため,次章で説明する同期処理を行わない場合は, 以下のように点線の矢印は省略する.
以下では,簡略化された プロセス管理として,Xinu のプロセス管理を説明する.
プロセスは、以下のような状態を持つ.
プロセスが生成された(create 関数) 後、休止(suspend) 状態と なる.さらに,ready 関数により,実行可能(ready)状態となる. その後,OS の時分割処理により(ctxsw 関数),実行状態と, 実行(current)状態を繰り返す.単一プロセッサシステムでは, 一時に、一つの処理しかできないので,実行状態となることができるプロセスは 一つである.実行可能状態や,実行状態となるプロセスから suspend 関数により 常に休止状態となることができる.このようにプロセスの状態の遷移を表した図 を状態遷移図(state transfer chart) という.
実行状態は,一時に一つのプロセスであるため,実行可能状態には,プロセスの 待ち行列ができる.この待ち行列をレディリスト(ready list) という. レディリストは,プロセス管理の要であり,並べられたプロセスは, 優先順位(priority) によって順序づけられている.この順番は, プロセスにどのように実行権を渡すか、という スケジューリングポリシー(scheduling policy) に関係している. もっとも簡単なスケジューリングポリシーは,レディリストに到着した順に、 FIFO(先入れ先だし)方式で出し入れする方式で、レディリストに並べられた プロセスに「平等に」実行権を渡すことができる.このスケジューリングポリシー は,ラウンドロビン(round robin) 方式という.または,総当り方式という. ラウンドロビン方式のプロセスに,優先順位がつけられている場合は, 優先順位の最も高いプロセスのみ(通常複数のプロセス) 「ラウンドロビン(総当り)」に実行権が与えられるが, 優先順位の低いプロセスは,より高いプロセスの実行がすべて終了するまで, 実行権が与えられることはない.Xinu のプロセスは,このような スケジューリングポリシーに基づいている.
レディリストは,プロセス全体を集めたプロセステーブル(process table) とは別に築かれる.プロセステーブルは,プロセス制御ブロック(process control block) (PCB) ともいう.以下のような key,next,prev を項目として持つ 配列で表現する.例えば,以下のような配列となる.
このキューは,
を表現している.項目 key にある数字をプロセスを識別する番号 (優先順位(priority)) とすると,この順番で レディリストに並べられることになる (優先順位が同じプロセスもあることに注意しょう).
このように,配列上で head,tail を生成することにより,自由に新たに キューを生成することができる.さらに,キューの操作は,配列にある ポインタを操作することにより処理することができる.
この配列は,q[] で定義され(q.hにある),newqueue 関数で新たに キューが生成される.enqueue 関数,dequeue 関数でキューを操作する. レディリストは,newqueue 関数で生成する.
プロセスは,プロセステーブルという表で管理される.各プロセスは, プロセスID という整数で識別される.
この配列は,proctab[] といい,全てのプロセスの情報が格納される.
Xinu のプロセス管理のもっとも中心的な関数として,ready 関数,suspend 関数, resume 関数,ctxsw 関数がある.プロセステーブルを管理するキューを実現する enqueue 関数,dequeue 関数,insert 関数がある.この他,時分割処理として, resched 関数を一定時間ごとに呼び出す関数があるが、実時間処理の章で説明する.
Linux や,FreeBSD などの Unix 系 OS では,ラウンドロビン方式のような単純な スケジューリングポリシーではなく,優先順位に順序づけられたレディリストを 持ち,優先順位が低いプロセスにも,一定の割合に応じた実行権が与えられる. そのため,プロセステーブルや,レディリストの構造もさらに複雑になり, コンテキストスイッチの際にも,さらに複雑な処理が必要になる. そのため,プロセスの数を多く生成すると,コンピュータ全体の処理が遅くなり, 実行の効率が落ち,重量プロセス(heavy weight process)といわれる (さらに,各プロセスが独立したメモリ空間をもつことも「重量」となる原因と なっている).
1980 年代よりこの問題を解決する一つの方法として,重量プロセスの中に, さらに,軽量プロセス(light weight process) を生成する 研究がなされ,そのようなプロセスをライトウエイトプロセスまたは, スレッド(thread)(以下,スレッドという) というようになった. Unix の重量プロセスとして,簡単な OS を実現し,プロセスを 生成する仕組みを実現したものである.
スレッドは,通常ラウンドロビン方式の簡単なスケジューリングポリシー で実行され,各スレッド間でメモリが共有されるので非常に実行効率が良く, 多くのスレッドを起動することができる.
Linux などで簡単にスレッドを生成することもできる.pthread というスレッドの 規格(IEEE規格) に基づいた,PTL というソフトウエアがある.
Unix 系OS など,プロセスに優先順位をつけることができる OS では, 優先順位に応じて,プロセスの実行時間を割当てる.
同じ優先順位を持ったプロセスには,同じ時間プロセスの実行を割り当てる方式 をラウンドロビン方式(round robin) という.