第11章 プロセス管理
プロセス
実行中のプログラムを管理する単位を プロセス と呼びます。シェル自身もプロセスです。以下、シェルがコマンドを実行した場合を例に、 プロセスの親子関係 と プロセスの生成から消滅までの流れ を示します。
- シェルからコマンドを実行
- シェルは子プロセスとして自分の生成(fork)
- シェルは子プロセスにコマンド実行(exec)を任せ、子プロセスの終了まで待機
- 子プロセスがコマンドを実行し終える
- 子プロセスが親プロセスに終了を伝えた後に消滅
- 親プロセスがシェルプロンプトを表示し、次のコマンドに備える
スケジューリング
Linux は複数のプロセスを同時に実行できる、マルチタスクの OS ですが、厳密には瞬間瞬間ではプロセスは1つしか実行されず、それぞれのプロセスの実行順序はスケジューラによって管理されています。
プロセスは Run Queue と呼ばれる待ち行列で待機し、 ラウンドロビン方式 で各プロセスが CPU を使用して処理を進めます。
また、プロセスは Nice 値というパラメータを持っていて、その値によって優先度が決まります。 -20 が最も優先度が高く、 19 が最も低くなっています。
ジョブ
Linux が実行中のプログラムを管理する単位である プロセス に対して、 ジョブ はシェルが管理するプログラムの単位です。シェルからコマンドを実行する場合、ジョブを フォアグランド か バックグラウンド に切り替える機能があります。
例えば、処理に時間のかかるジョブを実行する際、ジョブの完了を待つのではなく、その間に別の作業をしたりするはずです。そういった時に、ジョブをバックグラウンドで実行すれば、同じ端末から別のコマンドを実行することもできます。
コマンドをバックグラウンドで起動するには、コマンドの後ろに & をつけて実行します。実際に以下のコマンドを実行して、動作を確認してみましょう。
$ sleep 3600&
(3600秒スリープさせるジョブをバックグラウンドで実行。)
$ sleep 3601&
(3601秒スリープさせるジョブをバックグラウンドで実行。)
$ sleep 3602&
(3602秒スリープさせるジョブをバックグラウンドで実行。)
$ jobs
[1] Running sleep 3600 &
[2]- Running sleep 3601 &
[3]+ Running sleep 3602 &
(ジョブの状態を確認)
$ %1
sleep 3600
([1]のジョブをフォアグラウンドジョブにする)
^Z
[1]+ Stopped sleep 3600
(Ctrl+Z でフォアグラウンドジョブをサスペンドする)
$ jobs
[1]+ Stopped sleep 3600
[2] Running sleep 3601 &
[3]- Running sleep 3602 &
(ジョブの状態を確認)
$ %-
sleep 3602
(「-」のついたジョブをフォアグランドジョブにする)
^C
(Ctrl+C でフォアグラウンドにあるジョブを停止する)
$ %%
sleep 3600
(「+」のついたジョブをフォアグラウンドジョブにする)
^C
(Ctrl+C でフォアグラウンドにあるジョブを停止する)
$ fg
sleep 3601
(この場合の fg は、 fg %+, %+, %% と同じ動作になる)
(「+」のついたジョブをフォアグラウンドジョブにする)
^C
(Ctrl+C でフォアグラウンドにあるジョブを停止する)
プロセスID
Linux のプロセスには一意の ID である プロセスID が付与されます。現在実行されているプロセスのスナップショットを ps
コマンドで表示できます。
ps [オプション]
l
オプションを指定することで、 PID だけでなく PPID (親プロセスの ID ) も表示されます。実際にコマンドを実行してみましょう。
$ ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1041 1010 20 0 308196 24956 eq_pol Ssl+ tty1 0:05 /usr/bin/X :0 -background none -noreset -au
4 0 2589 2582 20 0 116568 2664 do_wai Ss pts/0 0:00 bash
0 0 3401 2589 20 0 148940 1444 - R+ pts/0 0:00 ps l
シグナル
Linux には、プロセスにシグナルというイベントを送信してプロセスを制御する機能があります。シグナルには、シグナル番号とシグナル名が割り当てられていて、代表的なものに以下のシグナルがあります。
シグナル番号 | シグナル名 | 意味 |
---|---|---|
1 | HUP | ハングアップ(Hang Up) |
2 | INT | 割り込み(Interrupt) |
3 | QUIT | 強制停止(Quit)。コアダンプ生成。 |
9 | KILL | 強制終了(Kill) |
15 | TERM | 終了(Terminate)。killコマンドのデフォルトシグナル |
ユーザがプロセスにシグナルを送信する方法が3つあります。
- シェルに割り当てられたキーの入力(例: ^C, ^Z, ^¥ など)
- kill コマンド(例: kill -HUP PID, kill -SIGINT PID, kill -9 PID など)
- プログラムで kill() 関数を呼ぶ
実際に以下のコマンドを実行して、動作を確認してみましょう。
まずは無限ループするシェルスクリプトを作成します。
#!/usr/bin/sh
while /bin/true
do
sleep 3600
done
$ chmod 755 loop
(シェルスクリプトに実行権を付与する)
$ ./loop
(3600秒のスリープを無限に繰り返す処理のため、フォアグラウンドで実行すると、シェルプロンプトが戻ってきません)
^C
(Ctrl+C を押すことで、 INT(Interrupt Singnal) が送信され、 loop を終了することができます)
次に先のシェルスクリプトに trap 処理を挿入します。
#!/usr/bin/sh
trap 'echo "...CTRL+C is pressed."' 2 # trap にて SIGINT(2) を受信した時に中止せず別の処理を実行
while /bin/true
do
sleep 3600
done
$ ./loop
^C...CTRL+C is pressed.
^C...CTRL+C is pressed.
(指定した trap 処理により、 Ctrl+C を押してもプロンプトが戻ってきません。)
^Z
[1]+ Stopped ./loop
(Ctrl+Z でサスペンドする)
$ kill %%
[1]+ Terminated ./loop
(kill コマンドでデフォルトの TERM シグナルを送信したことで、プロセスが終了した)