プロセスとジョブ

プロセスとジョブ, マルチタスクについて

gui では直感的に理解できるマルチタスクも cui ではその状態が見えにくい. そこで,ユーザによるプロセスやジョブの制御を学ぼう. まずプロセス制御とジョブ制御を明確に区別して学習し,理解してからそれらを使いこなすことを目的とする.

マルチタスクとは

同時に複数の処理をこなすこと,もしくはそれらの処理.
今や普通に触れる OS のほとんどはマルチタスクなシステムである.

プロセスとは

プロセス

一つのプログラムを良くみると様々な機能から構成されている. そこで,こうした機能毎に動作を分離することで,様々な処理を能率良く,かつより分かりやすく安全に行なうことができる.
この時の,コンピュータ上で自立して動作する最小単位をプロセスと言う. 言い換えると,一つのプログラムは複数もしくは一つのプロセスからなる.
プロセスをさらに細分化した「スレッド」という概念もあるが 自立して動作しないものであり,ここでは触れない.

プロセス制御

unix 上ではほぼ常に多数のプロセスが(擬似的に)同時に動作している. この時,個々のプロセスを一時停止ないしは停止したり,優先順位を上下させたりすることができると何かと便利だ. 実際にそのようにプロセスの動作をその場で変えることをプロセス制御という.

プロセスの親子関係

unix では,全てのプロセスはなんらかのプロセスによって生成される. これを「親子関係」と見なして,もとのプロセスを新しいプロセスの 「親プロセス」と呼ぶ.
「子プロセス」の「死体処理(ゾンビ状態のプロセスを消滅させること)」をするのは親プロセスの役目であるため, 子プロセスが親プロセスよりも先に死ななければいけない.
よって,何らかの理由により親プロセスが先に死んでしまった場合, ゾンビになった子プロセスの処理が難しくなることがある.

プロセスの状態を知る: ps コマンド

プロセスの状態を知るには, ps コマンドを用いる. 詳細は以下のとおり.

  • オプション無しだと「自分が実行した」プロセスのみが表示される.
  • お勧めのオプションは,システム全体のプロセスがわかり,かつ, BSD でも linux でも cygwin でも有効な axu である.
    ただし,cygwin システムはほとんどプロセスを持っていないため,ps axu では寂しい結果しか表示されない.
  • cygwin では ps -W とすると,MS-Windows のプロセスも表示されるので,この方が良いかもしれない.

  cygwin と(仮想環境) Linux の両方で ps axu を試してみよう.
また、念のため、man ps として、オンラインマニュアルを一瞥しておこう.

ps コマンドの使い方による違いの例

単に ps とすると…

  PID TTY          TIME CMD
  29452 pts/0    00:00:00 sh
  29475 pts/0    00:00:00 tcsh
  31982 pts/0    00:00:00 ps

という感じだが、ps axu とすると

  USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
  root         1  0.0  0.0  1504  352 ?        S     2010   0:01 init [2]
  root         2  0.0  0.0     0    0 ?        SN    2010   0:00 [ksoftirqd/0]
  root         3  0.0  0.0     0    0 ?        S<    2010   0:00 [events/0]
  …略…
  daemon   10220  0.0  1.2 16972 11564 ?       S    23:29   0:00 /usr/local/apache2/bin/httpd -k start
  paoon    10221  0.0  0.0  2496  848 pts/0    R+   23:30   0:00 ps axu

と表示され、「他人の」プロセス情報も見ることが出来る.

ps コマンドで見られる情報のうち,重要なものだけ以下に解説しておこう.

情報名 内容
USER ユーザ名. そのプロセスの利用者.
注) 通常ユーザは,自分が利用しているプロセスしか制御できない.
PID プロセス ID. 各々のプロセスに固有な番号.
プロセスの制御はこの PID を基本として行なわれる.
%CPU cpu 利用率.
%MEM メモリ使用率.
tty 制御端末名. そのプロセスがどの「端末」に属しているかを示す.
STAT プロセスの状態. トラブル時に役に立つ!
表示文字の意味は以下の通り(主要なもの).

“S” … プロセスは sleep 状態(キーボード入力完了待ち)である.
“R” … プロセスは実行可能状態.
“D” … プロセスはディスク入出力完了待ちである.
“T” … プロセスは停止(stop)中である.
“Z” … プロセスはゾンビ(zombie, “死んでいる”)である.

T や Z という状態は異常事態を示唆する. また,長時間 D のままという場合も,何かおかしい可能性が高い.
command コマンド名と引数

  ps コマンドを用いて,今自分が実行している複数のプロセス全てについて,その状態がどうなっているかきちんと調べよ.



プロセスの状態を知る: top コマンド

プロセスの状態を知るには, top コマンドも使える.詳細は以下のとおり.

  • top コマンドで,プロセスの状態を「動的」かつ「連続的」にみることができる.
  • 通常は,CPU利用率の高い順にプロセスが動的に並べ替えられて表示される.
  • この「並べ替えに使う項目」は、top 実行中に F キーを押せば変えることが出来る.

  top コマンドを実行し,挙動を見てみる.また,実行中に ? キーを押し,実行中に何ができるか把握せよ.



プロセスを制御: プロセスを作る,動作させる

ユーザから見たとき,プログラムを実行することは即ちプロセスを生成することである. 逆に,プログラムの実行が完全に終わったときには, そのプログラムによって作られたプロセスは全て消滅したことになる.

プロセスを制御: プロセスの優先度を変更する

プロセスには優先順位があり,優先順位が高い方がより先に実行されるようになっている. この仕組みにより,様々なプロセスが同時に動作しても人間にとって快適に扱えるようになっている.

優先順位を示す指標

意味
スケジューリング優先度 0 〜 127 の数字で,小さい方が優先される.
nice 値 この数字とプロセスの性格などから, 上の優先度が決定される.
-20 〜 20 程度の数字で,小さい方が優先される.デフォルトは 0.

プロセスの優先順位を知る: ps -l (ps ハイフン エル) コマンド

プロセスの優先度と nice 値を見るには, ps -l (← ps に対し、ハイフンと小文字のL(エル)をオプションとして与えている) とするのが簡単である.
表示カラムのうち,”PRI” が優先度(Priority), “NI” が nice値 である.

優先順位を変更する

プロセスの優先順位を変える: nice/renice コマンド

プロセスの優先順位を変えることも出来る. 重いプログラムを走らせる時などは、この操作を知っていると助かることも多い. 詳細は以下のとおり.

コマンド 解説
nice プログラム起動時に nice 値を決定するコマンド.
デフォルトでは 10 だけ nice値を上げる(= 優先度を下げる).
通常ユーザは優先度を低くすることしか出来ないが、
管理者は優先度を上げることも出来る.

注) csh 系のシェルに nice コマンドが組み込まれていることがある.
システムの nice コマンド( /bin/nice )とかぶるので混同しやすい.
事前にマニュアルを見るなどして、気をつけよう.
renice 既に動いているプロセスの nice 値を変更するコマンド.
通常ユーザの制限は nice と同様.

使い方の例 (詳しくはマニュアルを見よう):
nice については、基本は nice 起動したいコマンド とするだけである. 例えば、 hoge というプログラムを, 通常より nice 値を 10 大きくして(= 優先順位が下がる)起動するときは

/bin/nice hoge

とすればよい.

既に起動しているプログラムがあるとして、その nice 値を変える例も見てみよう. まず、そのプロセスの PID と nice 値を ps -l コマンドを使って確認してみる.

$ ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S   510  4846  4845  0  80   0 - 29208 rt_sig pts/0    00:00:00 tcsh
0 T   510  4862  4846  0  80   0 - 62522 signal pts/0    00:00:00 emacs
0 R   510  4863  4846  0  80   0 - 27033 -      pts/0    00:00:00 ps

と表示されるので、 例えばそのプログラムが emacs だとすると、PID は 4862, nice 値は 0 ということが分かる.

renice コマンドは、 renice nice値 PID という形で用いるので、

renice +10 4862

とすると、

4862: 古い優先度は 0、新たな優先度は 10 です

と表示が出て、優先度が変更されたとメッセージが出る(優先順位が下がるので、日本語としては誤解を招くメッセージだが!). 実際に、ps -l コマンドで確認してみると、

$ ps -l
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S   510  4846  4845  0  80   0 - 29208 rt_sig pts/0    00:00:00 tcsh
0 T   510  4862  4846  0  90  10 - 62522 signal pts/0    00:00:00 emacs
0 R   510  4884  4846  1  80   0 - 27034 -      pts/0    00:00:00 ps

emacs の priority値と nice値 が確かに 10 だけ大きくなっている.

試しに、優先順位を上げようとして

renice -10 4862

とすると、

renice: 4862: setpriority: 許可がありません

と言われて、優先順位を上げることが出来ないことも確認できる.



プロセスを終了させる: kill コマンド

プロセスを停止させるには kill コマンドを使う.詳細は以下のとおり.

unix のプロセスには「プロセス間通信」といってプロセス同士が通信する仕組みが備わっている. このプロセス間通信のうち,メッセージを伝えるだけの機能を持つものとして「シグナル」がある. このシグナルをプロセス宛に送りつけることによって,プロセスを一時停止させたり,再起動させたり,終了させたりできる.

シグナルをプロセスに送りつけるには,

kill -シグナル pid

とする. シグナルは次の表の「シグナル名」か「シグナル番号」で指定する.
なお、シグナルを省略すると TERM (=15) を指定したことになる.

シグナル番号 シグナル名 シグナルの意味
15 TERM TERMinate. 終了.
“kill” コマンドのデフォルト.
9 KILL KILL. 「強制」終了.
このシグナルを受け取ったプロセスは終了することになっている.
2 INT INTerrupt. 端末から割り込み(“CTRL-c”)を受けた,という意味.
このシグナルを受けたプロセスは終了する.
3 QUIT QUIT. 端末から中断終了(“CTRL-\“)を受けた,という意味.
このシグナルを受けるとプロセスは(core を吐いて)死ぬ.
18 TSTP TerminalSToP. 端末から停止(“CTRL-z”)を受けた,という意味.
このシグナルを受けるとプロセスは一時停止する.
17 STOP STOP. 強制一時停止. このシグナルを受けるとプロセスは一時停止する.
19 CONT CONTinue. 停止状態のプロセスの実行を再開する.
1 HUP HangUP. 端末回線が切れた,ということを意味する.
普通のプロセスはこのシグナルを受けると実行を中止するが,
再起動したり特別なモードに移行するプロセスも多いので注意が必要.

この表から分かるように,そのプロセスを確実に止めたい場合は,

kill -9 pid

とするか,

kill -KILL pid

とすればよい.

注意: シグナル “HUP” や pid “1” “0” “-1” は特別な意味で使われることが多いので,よくわからない場合は使わないように注意すること. 酷い目にあってみたい,という酔狂な人は自宅の unix などでスーパーユーザになって,
kill 1
kill -KILL -1
などといろいろやってみよう(^-^).

注意: state が D のプロセスや,zombie プロセスには kill -KILL が有効でない場合がある. こういう場合は,特に支障がなければ放置しておくことになるが(^-^)、まあ、様子を見て、支障がありそうだったら管理者に相談しよう.



接続が切れたことを無視させる: nohup コマンド

ユーザがログアウトしても動き続けるようにプログラムを起動することが出来る.

プログラムが動作中であっても,端末を終了したりログアウトしたりすると通常はそのプログラムは終了してしまう. これは,そのプログラムを起動した端末環境が,終了する直前にそのプログラムに対して上の “HUP” シグナルを送っているためである.

しかし,長時間の計算をするときなど, 「ログアウトした後もプログラムは動きつづけたままでいて欲しい」 ということがある. こうしたときには, nohup コマンドを用いて nohup コマンド名 としてプログラムを起動すると,ログアウトしてもこのプログラムが動作し続けるようにできる.

注意: コマンド指定無しで単に nohup と起動しないこと! こうしまうと,それ以降のコマンド全てが nohup 扱いになり大きなトラブルを引き起こす可能性が高い.

  次の手順でプロセス制御を試してみよう.

  1. 端末エミュレータ中で emacs -nw と入力して emacs を起動する.
  2. emacs が起動している状態で CTRL-z として,emacs を停止状態にする(CTRL-z や停止状態の説明は後述).
  3. ps コマンドを試してみて,emacs の存在とその状態を把握する.
  4. top コマンドを試してみて,emacs の存在とその状態を把握する.
  5. ps コマンドで emacs の PID を把握し,kill コマンドを用いて emacs を強制終了させる.
  6. psコマンドで emacs が存在しない(強制終了したため)ことを確認する.



ジョブ

ジョブとは,(シェルから見た)コンピュータ上で実行される実態の単位の概念で, 「一連の仕事をしている(複数の)プロセス」 のことである.
まあ簡単に言えば,シェルで命令した「(一行分の)命令の固まり」であり, 人間にとって一つの処理と見なせるもの,と思えばよい. シェルの history コマンドで「一回分」のコマンドと思ってもよい.

フォアグラウンド,バックグラウンド

シェルは人間と「対話的」に動作するため,そのまま複数のプログラムを同時に動作させるわけにはいかない.
そこで,「人間と対話しながら作業するジョブを一つだけ」と 「そうでないジョブ(複数)」 をわけることでマルチタスクを可能としている.
その全体の関係は下の図のようになっている.

job status

ジョブ状態 説明
フォアグラウンド シェルを通じて人間からのキーボード入力を受けるジョブの状態.
つまり,通常使っている状態のジョブはフォアグラウンドにいるのである.
端末1つにキーボードは1つしかないので,フォアグラウンドジョブは端末1つにつき,1つしか存在できない.
バックグラウンド キーボードと関係なく、背後で動作している状態.
この状態で動作するジョブはいくつあってもよい.
停止状態 起動はしているが,一時停止している状態.
ジョブをバックグラウンドへ持っていくときの途中状態としても用いる.

ジョブの状態を知る: jobs コマンド

ジョブの状態を知るには, jobs コマンドを用いる. 詳細は以下のとおり.

例えば、三つほど一時停止 or 裏で動いているジョブがある状態で、jobs とすると、次のような出力を得られる.

$ jobs
[1]  - 中断       emacs -nw
[2]  - 実行中  sleep 1000 &
[3]  + 中断        man less

この表示は左から,
“ジョブ番号 状態 コマンドと引数”
というリストになっている. ジョブ番号に「+」がついているのは,ジョブ操作コマンドのデフォルトの対象がこれであることを意味している. (だいたいは一番大きい番号だ)

ちなみに,この例では二つのジョブが「中断」状態,つまり「停止状態」で, 一つが「実行中」、つまりバックグラウンドに居ることがわかる.

  端末エミュレータ中で emacs -nw &sleep 1000 & などを何度か入力するなどして, jobs コマンドの様子を試してみよ.



ジョブ制御

ジョブ制御とは,ジョブを三状態のある状態から他の状態へと移動させることや, プロセス制御と同じくジョブを「殺したり」することを言う.

ジョブには大まかにいって上に載せた図のような三つの状態がある. 図中にあるようなコマンドを用いてジョブの状態を変遷させることが ジョブ制御の中心となる. 詳細は以下の通り.

ジョブ制御: ジョブを作る

& をつけて起動するとそのジョブはバックグラウンド状態に

並列でジョブを走らせるとか Window 環境で「別窓」を開きたい時はこうする

シェルを通じてプログラムを実行したとき, シェルに投入された一塊のコマンド(群)がジョブとして生成される. この時, 起動オプションとして & をつけるかどうかで、ジョブの状態が次のように変わる.

& をつけない → そのジョブはフォアグラウンド状態で起動する.
& をつける → そのジョブはバックグラウンド状態で起動する.

window system が動作しているときに 端末中で “emacs &” とすると,emacs が「別窓で」起動するのが見られるが,これがそうである.



ジョブ制御: ジョブの状態を変える

以下、ジョブ制御のコマンドを紹介しよう.

なお、ジョブを フォアからバックに直接移す方法は無い. フォア → 停止 → バック と経由する方法( CTRL-zbg %ジョブ番号 というコンボ)があるのでそちらを使おう.

バック/停止 ジョブをフォアへ: fg コマンド

バックグラウンドで動いている or 停止しているジョブと「対話」するにはフォアへ持ってくる

fg コマンドで fg %ジョブ番号 とすると、そのジョブ番号をもつジョブをフォアグラウンドへ移すことができる. これでキーボードでそのジョブと対話が可能になる.

フォアに居るジョブを停止: CTRL-z キー

今「対話」しているするジョブを停止するには CTRL-z キーを使う.

ただし,シェルそのものだけはこれで停止しないようになっている(例外). シェルを停止したい場合は, suspend コマンドを使う.

ジョブを バック ←→ 停止 の間を行き来させる

見えてないジョブを、動かしたり停めたり出来る

コマンド 説明
bg %ジョブ番号 そのジョブ番号をもつジョブをバックグラウンドに移す(動作させる)
stop %ジョブ番号 (stop コマンドを内蔵するシェルの場合)
kill -STOP %ジョブ番号 (そうでないシェルの場合)
そのジョブ番号をもつジョブを停止状態にする(一時停止)

注: 近年、システムに上のものとは役割が異なる stop コマンドが実装されていることがあり、そういうシステムでは一部のシェルに stop コマンドが内蔵されていなかったりする. stop コマンドを試してみて unknown job というエラーが出る場合はこれに該当するので、kill -STOP %ジョブ番号 を使おう.

ジョブを殺す: kill コマンド

ジョブ単位でも kill コマンドが使える

jobs コマンドでわかるジョブ番号を用いて、
kill %ジョブ番号
とすればそのジョブを殺す(一時停止ではない、完全な停止)ことができる.

注意: 以前示したように、フォアグラウンドジョブ,即ち,人間と対話しながら動作しているジョブは CTRL-c などでも殺すことができる. ただし,シェルそのものだけはこれで死なないようになっている(例外).

  次の手順でジョブ制御を試してみよう.

  1. touch コマンドを用いてファイルを新しく作る.
  2. 作ったファイルを 端末中で vi か emacs で編集し(“ジョブの投入”),適当に数行書き込む.
  3. vi or emacs を終了せずに,停止させることでシェルへ戻る(“ジョブの停止”).
  4. less コマンドを用いて編集したファイルを見る(“ジョブの投入 & 停止”).
  5. date コマンドを用いて日時を調べて…(“ジョブの投入 & 停止”).
  6. fg コマンドを用いて vi or emacs へ戻り(“停止ジョブをフォアグラウンドへ”)
  7. 先ほど調べた日時を書き込んでから, vi or emacs を終了(“ジョブの停止”).
  8. 再び, less コマンドを用いて編集したファイルを見て,先ほどの日時書き込みが保存されていることを確認(“ジョブの投入 & 停止”).

レポート

以下の課題について能う限り賢明な調査と考察を行い,
“AppliedMath7-Report-02”
という題名をつけて e-mail にて教官宛にレポートとして提出せよ. なお,レポートを e-mail の代わりに TeX で作成した書面にて提出してもよい.

課題

  1. 本資料の実習部分をすべて行い,それについて「きちんと」報告せよ(やりました,などだけというのはダメだよね).
  2. ジョブを投入して停止状態にする方法を複数述べよ.
    また,これらの作業を実際にやってみよ. なお,この操作の様子を script コマンドで記録しておいて,その記録を提出する形で報告すること.
    注: script コマンドはとても便利! なので、マニュアルなどでぜひ調べよう.
  3. kill でもプロセスが殺せない場合が何通りか考えられる. そのうち,3種類を挙げてきちんと解説せよ.
  4. ジョブを あわせて 5つほど投入し,全てが共存する状態にし,三状態を自在に遷移させてみよう.
    なお,この操作の様子を script コマンドで記録しておいて,その記録を提出する形で報告すること.