Linux標準教科書 第11章
プロセス管理
11.1 プロセスとは
Linux では実行中のプログラム(アプリケーション)を管理する単位をプロセスと呼びます。シェル自身もプロセスです。「ユーザがシェルからコマンドを実行すると、シェルは子プロセスとして自分の分身を作ります(fork)。次に、シェルは子プロセスにコマンドの実行(exec) を任せ、子プロセスの終了を待ちます。子プロセスはコマンドの実行を終えると親プロセスに終了を伝え、消滅します。親プロセスは子プロセスの終了を受け取り、シェルプロンプトを表示し、ユーザの次のコマンドに備えます。
11.2 スケジューリング
それぞれのプロセスの実行順序は Linux のスケジューラによって管理されています。各プロセスはキューで待機しますが、プロセスによっては優先度の高いもの、それほど高くないものもあります。そのパラメータの一つに Nice 値があります。Nice 値は、-20 から 19 までの値を取り、-20 が最も実行優先度が高く、19 が最も低くなっています。nice コマンドにより実行優先度をプロセスに指定したり、実行中のプロセスについては、renice コマンドにより優先度を変更することができます。
11.3 フォアグランドジョブとバックグランドジョブ
プロセスとよく似た管理単位としてジョブがあります。Linux が実行中のプログラムを管理する単位であるプロセスに対して、ジョブはシェルが管理するプログラムの単位です。シェルからコマンドを実行する場合、ジョブをフォアグランドとバックグランドに切り替える機能があります。パイプを使って複数のプロセスを実行する事ができます(ジョブは一つの単位となります)。
コマンドをバックグランドで起動するには、コマンドの後ろに & (アンパサンド)をつけて実行します。
$ my_heavy_script &
実行中のコマンドをバックグランドに切り替えるには、 ^Z ( CTRL+Z ) でサスペンドしてから、bg コマンドでバックグランド実行を継続させます。fg コマンドでフォアグランドに戻すこともできます。ジョブの状態は、jobs コマンドで確認する事ができます。
実習: 複数のジョブを実行して、切り替えを試してみましょう。
$ sleep 3600& (1時間(3600秒)スリープするプロセス)
$ sleep 3601&
$ sleep 3602&
$ jobs
[1] 実行中 sleep 3600
[2]- 実行中 sleep 3601 &
[3]+ 実行中 sleep 3602 &
$ %1 ([1]のジョブをフォアグランドジョブにします)
sleep 3600
^Z (CTRL+Z でフォアグランドをサスペンドします )
[1]+ 停止 sleep 3600
$ jobs
[1]+ 停止 sleep 3600
[2] 実行中 sleep 3601 &
[3]- 実行中 sleep 3602 &
$ %- ( – のついたジョブをフォアグランドジョブにします)
sleep 3602
^C (フォアグランドにあるジョブを停止します)
$ %% ( + のついたジョブをフォアグランドジョブにします)
sleep 3600
^C (フォアグランドにあるジョブを停止します)
$ fg (この場合の fg は fg %+ , %+, %% と同じ動作になります)
sleep 3601
^C (フォアグランドにあるジョブを停止します)
11.4 プロセス ID
Linux のプロセスには、一意の ID であるプロセス ID(PID) がふよされます。自身(シェル)の PID は && で取得できます。
$ echo $$
5093
$ ps [オプション] 現在実行されているプロセスのスナップショットを表示します
$ sleep 3600 &
[1] 5775
$ ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 1000 5093 5092 20 0 115496 2124 wait S pts/0 0:00 -bash
0 1000 5775 5093 20 0 107892 612 hrtime S pts/0 0:00 sleep 3600
0 1000 5776 5093 20 0 121276 028 – R+ pts/0 0:00 ps l
ps に l オプションを指定すると、PID だけでなく PPID( 親プロセスの ID ) も表示されます。 ps の親プロセスが -bash である事がわかります。
$ jobs
[1]- 実行中 sleep 3600 &
$ %-
sleep 3600
^Z
[1]+ 停止 sleep 3600
$ jobs
[1]+ 停止 sleep 3600
11.5 シグナル
Linux には、プロセスにシグナルといイベントを送信してプロセスを制御する機能があります。シグナル番号およびシグナル名が割り当てられており、代表的なものに以下のシグナルがあります。
ユーザがプロセスにシグナルを送信する方法は3つあります。
・シェルに割り当てられたキーの入力(例 : ^C, ^Z, ^\ など)
・kill コマンド(例 : kill-HUP PID, kill-SIGINT PID, kill-9 PID など)
・プログラムで kill() 関数を呼ぶ
kill -l コマンドでシグナルの種類を表示する事ができます。
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
実習: 無限ループするシェルスクリプトを作成します。
$ vi loop
#!/usr/bin/sh
while /bin/true
do
sleep 3600
done
:w
^Z
$ chmod 755 loop
$ ./loop
(シェルスクリプトが帰ってきません)
^C ← CTRL+C を押す事で、INT(Interupt Signal) が送信され、loop を終了する事ができます
$
先ほど作成したシェルスクリプト”loop” に trap 処理を挿入します。
$ fg
#!/usr/bin/sh
trap ‘echo “…CTRL+C is pressed.”‘ 2 #trap にてSIGINT(2)を受信した時に中止
while /bin/true
do
sleep 3600
done
:w
^Z
$ ./loop ← 無限ループに入ったので、^C を推しますがプロンプトが戻りません
^C…CTRL+C is pressed.
^C…CTRL+C is pressed.
^Z ← ^Z でサスペンドして kill コマンドでデフォルトの TERM シグナルを送信
[3]+ 停止 ./loop
$ kill %%
[3]+ 停止 ./loop ← 終了しました
実習: ps alx を実行して、PID が一番小さなプロセスが何か確認してみましょう。
$ ps alx
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1 0 20 0 54192 3912 ep_pol Ss ? 0:00 /usr/lib/systemd
1 0 2 0 20 0 0 0 kthrea S ? 0:00 [kthreadd]
1 0 3 2 20 0 0 0 smpboo S ? 0:02 [ksoftirqd/0]
1 0 5 2 0 -20 0 0 worker S< ? 0:00 [kworker/0:0H]
実習: sleep コマンドを2つ、バックグランドで実行し、ps l で親子関係を確認しましょう
$ sleep 3600 &
[3] 6114
$ sleep 3601 &
[4] 6115
$ ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 3500 3499 20 0 115656 2164 wait Ss pts/0 0:00 -bash
4 1000 5093 5092 20 0 115656 2212 wait S pts/0 0:00 -bash
0 1000 6114 5093 20 0 107892 612 hrtime S pts/0 0:00 sleep 3600
0 1000 6115 5093 20 0 107892 612 hrtime S pts/0 0:00 sleep 3601
$ kill -9 6114 6115
$ ps l
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
0 1000 3500 3499 20 0 115656 2164 wait Ss pts/0 0:00 -bash
4 1000 5093 5092 20 0 115656 2212 wait S pts/0 0:00 -bash
[3] 強制終了 sleep 3600
[4]- 強制終了 sleep 3601
実習: stty -a コマンドで、シグナルとキーの割り当てを確認してみましょう。
$ stty -a
speed 9600 baud; rows 29; columns 85; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?;
swtch = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc
ixany imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe -echok -echonl -noflsh -xcase -tostop -echoprt echoctl
echoke
11.6 top コマンドと pstree コマンド
ps コマンドの他にプロセスの状態を表示するコマンドとして top コマンドがあります。top コマンドは実行中のプロセスの状態をリアルタイムで表示します。
実習: 実行中のシェルを起点として、プロセスをツリー状に表示してみましょう。
# pstree $$
bash───pstree
topコマンド実行中に「k」キーを実行すると、指定したPIDのプロセスをkillすることが出来ます。
「PID to signal/kill」という表示がされるので、そこにkillしたいプロセスIDを入力する。
# top
top – 02:48:56 up 3 days, 6:29, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 91 total, 2 running, 89 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1017480 total, 703300 free, 113452 used, 200728 buff/cache
KiB Swap: 1040380 total, 1040380 free, 0 used. 761764 avail Mem
PID to signal/kill [default pid = 1] ここに kill したいプロセス ID を入力
11.7 プロセス間通信
Linux 上である処理を完了するのに、1つのプロセスを完結する場合もありますが、複数のプロセスがコミュニケーションを取りながら同期したり、動作を変更して達成する事が多々あります。これをプロセス間通信と呼びます。
次のような特徴があります。
・パイプ:1つのプロセスの標準出力をつなぎ替えます。パイプ( | )の左側のコマンド(プロセス)の標準出力を右側のコマンド(プロセス)の標準入力につなぐ事で、通信を実現します。
・シグナル:特定のシグナルの受信を待って特定の処理を開始するなど、同期を実現します。
・共有メモリ( shared memory ):特定のメモリ領域を複数のプロセスで共有する事でメッセージの受け渡し等が行えます。
・セマフォ( semaphore ):資源のロックを行い、複数のプロセス間で同時に書き込みしたりしないように排他処理を実現します。
・メッセージキュー( message queue ):キューにメッセージを格納しておき、複数のプロセス間での非同期の通信を実現します。
・ソケット:ネットワーク経由のホスト間のプロセスでの通信を実現します。
11.8 章末テスト
(1)作成中のプログラムをシェルプロンプトから実行したところ、プロンプトが返って来なくなった。どのような対処方法があるか。
(2)シグナル番号とシグナル名の組み合わせとして正しいのは次のうちどれか。
- SIGINT:1, SIGHUP:2, SIGTERM:9, SIGKILL:15
- SIGHUP:1, SIGINT:2, SIGTERM:9, SIGKILL:15
- SIGHUP:1, SIGINT:2, SIGKILL:9, SIGTERM:15
- SIGHUP:1, SIGINT:2, SIGTERM:9, SIGKILL:15
章末テスト解答
(1)強制的にCtrl + cで抜けます。PID(調べて)をKillします。
# ps -l
# kill -9 (PID番号)
(2)3