プロセスは、生成されてから消滅するまでに3つの状態の間を遷移します。
プロセスは、システムの内部ではプロセス制御ブロック(PCB:Process Control Block)と呼ばれるデータ構造で表されます。
PCBはPCB識別子、プロセス優先順位、プログラムカウンタ、
レジスタの退避領域などを格納しています。
実行状態でないプロセスは、中断したときの(プログラムカウンタ、レジスタなど)
情報を退避し、実行状態に戻るときこれらの退避情報をもとに、
中断された直前に回復します。
あるプロセスから他のプロセスへCPU使用権を切替える一連の処理 (ディスパッチとプリエンプションの繰返し)をコンテキストスイッチと呼ぶ。
実行状態にあるプロセスはプロセッサごとに1つです。
他の複数のプロセスは実行可能状態か待ち状態です。
このために、実行可能状態にあるプロセスと待ち状態にあるプロセスに関して、
それぞれ実行可能キュー(CPU使用権が回ってくるのを順番に待つ)
と待ち状態キューと呼ばれる PCB のキューが作られる。
プロセス用の領域は、コード領域、データ領域、スタック領域からなります。 プロセス用の領域を確保するには2通りの方法があり、 1つは親プロセスと子プロセスでコード領域だけを共有し、 データ領域とスタック領域をコピーする方法です。 もう1つは親プロセスと子プロセスでコード領域とデータ領域を共有し、 スタック領域のみをコピーする方法です。
後述するスレッドを実現する際にも使用されます。
中断されたプロセスは、
システムあるいは他のプロセスによって再開されるまで前に進むことができない。
プロセスの中断が長時間続く場合は、
プロセスが使用している資源を解放しなければならない。
プロセスを効率よく実行するためには、プロセス自体の高速化のみならず、 プロセスの処理の本質(プロセス生成、消滅、コンテキストスイッチなど) に関係しない操作の時間を極力小さくしなければならない。
このため、OSはスレッド(thread)
と呼ばれるプロセスの処理の本質とは関係ない操作の時間を、
(並行処理するなどして)小さくするための機構を実現しています。
プロセスの処理の本質とは関係ない部分で細分化された各スレッドは、
制御の流れを表し、実行に必要なレジスタセットとスタックを持つ。
単一スレッドプログラムの実行(制御)の流れは、
一通り(一筆書き)の流れとなります。
一方、多重スレッドプログラムの実行(制御)の流れは、
多重の流れとなります。
ユーザレベル(Javaなどのマルチスレッド機構)で多重スレッドを実現する場合、 スレッドごとにスタックとレジスタを確保し、 スレッドを切り替えるときには、プログラムカウンタ、レジスタの値、 スタックポインタだけを退避、回復すればよい。
スレッドの切り替えはプロセスの切り替えに比べ、システムにかかる負荷が小さい。 また、サーバプログラムを多重スレッドのプロセスとして構成することによって、 クライアントの要求に対し応答性が向上します。
たとえば、クライアントからサーバに対して要求があるたびに、
その要求に対応したスレッドを生成し(本質的な処理)、
そのスレッドにクライアントが要求した処理とクライアントへの結果の応答を任せる
(スレッドはプロセスの処理の本質とは関係のない操作、制御を行う)。
このようにすることによって、サーバはスレッドを生成した後、
直ちに他のクライアントの要求を受け付けることができる。
しかし、多重スレッドプログラムを作成するには、
プロセスの持つ資源を排他制御するなど、
単一スレッドにはない問題を考慮する必要があります。
プロセスはCPU処理と入出力制御が交互に現れる処理の系列と考えることができる。
プロセスの実行は、まず、CPUの処理から始まる。次に入出力処理が続く。
以降は、この順に処理が繰り返され、最終的にCPU処理で実行が完了します。
プロセスが入出力待ちになると、CPUはアイドル状態となります。
マルチプログラミング(multi programming)は、CPUのアイドル時間を減らし、 CPUの利用率向上を目的としています。 このため、複数のプロセスが同時に主記憶上に置かれ、 実行中だったプロセスが待ち状態になると、 すかさず他のプロセスがCPU使用権を奪います。 したがってCPUはアイドル状態になりにくくなります。
マルチプログラミングにおいて、
CPU処理の割合が入出力処理の割合より多いプロセスを
CPUバウンドのプロセスと呼ぶ。
逆に入出力処理の割合がCPU
処理の割合より多いプロセスを入出力バウンドのプロセスと呼ぶ。
CPUスケジューラは、実行可能状態にあるプロセスの集合から、
次にCPUを割り付けるプロセスをある基準(スケジューリングアルゴリズム)
によって選択します。
選択されたプロセスにCPU
割り付けを行うのはディスパッチャの役目です。
CPUスケジューラがプロセスを選択する場合、 各スケジューリングアルゴリズムの特長を考慮する必要があります。
複数のスケジューリングアルゴリズムを比較する要素として以下のものがあります。
スケジューリングアルゴリズムは横取り不可能(nonpreemptive)なものと横取り可能 (preemptive)なものに分けることができる。
前者はCPUがあるプロセスに割り付けられると、 そのプロセスが終了するあるいは入出力完了待ちになる(制御がOSに戻る)まで、 他のプロセスにCPUを割り付けることができない。
後者はプロセスが実行中であっても、CPU使用権を奪い取り、 他のプロセスに割り当てることができる。
・ 横取り不可能…到着順方式、SJF方式
・ 横取り可能…優先度方式、ラウンドロビン方式
この方式では、 処理時間の長いプロセスが先に到着すると平均ターンアラウンドタイムは大きくなり、 逆に処理時間の短いプロセスが先に到着すると平均ターンアラウンドタイムは小さくなります。
到着順方式は、プロセスの実行可能キューを
FIFOキューとして構成することにより実現されます。
各プロセスに割り付けられる優先度は、 システム内部(デッドライン、入出力時間とCPU時間)、 外部(緊急のイベント)の基準によって定義されます。
優先度方式での問題は飢餓(starvation)と呼ばれる、
プロセス永久待ち状態が発生することです。
つまり、優先度の低いプロセスがいつになっても実行できないことです。
この問題を解決する方法としてエージング(aging)があります。
エージングは長時間システム内に滞在しているプロセスの優先度を徐々に高くする方法です。
実行中のプロセスは時間(タイムスライス)が満了すると、
実行可能キュー(FIFOキュー)の最後につながれ、
次にCPU使用権が与えれれるまで待つ。
カーネルはプロセスについて、 主に次のような管理を行ないます。
カーネルは生成されたプロセスに対して PID(Process ID) という番号を昇順にふり識別管理します。 プログラムの実行が終了してもその PID は永久欠番になり、再使用されません。 ただし、PID が最大数を超えるとまた循環して使われます。
PID の他、メモリ上でのアドレス、プロセスの状態、 実行時間などの情報は、 カーネル内のプロセステーブルに格納され、管理に利用されます。
スケジューリングとは CPU を割り当てるプロセスを決定したり切り替えることです。
プロセスにはそれぞれプライオリティ(優先度)があり、
この値を基準にしてスケジューリングが行われます。
プロセスはこのスケジューリングに従い、
実行状態と実行待ちの状態を繰り返しながら、通常は終了へと向かいます。
カーネルは、プロセスをどれだけメモリに読み込むかを決めます。
メモリに読みきれないプロセスに対しては2次記憶へのスワッピングが行われます。
2次記憶に出すことをスワップアウト、
2次記憶からメモリへ戻すことをスワップインと言います。
このメモリ管理によって、実際のメモリサイズよりも大きなプロセスの実行や、
複数のプロセスの効率的な実行が可能になります。
詳細はメモリ管理を参照してください。