03. プロセスとジョブ

Gemini generated image

この回で何を学ぶか

今回の授業では以下のことができるようになることが学習の目標である.

  1. Unix における「プロセス」と「ジョブ」の違いを説明できる.
  2. ps, top, jobs を用いて,現在動いている処理の状態を確認できる.
  3. Ctrl-C, Ctrl-Z, &, fg, bg, kill を使って,ジョブやプロセスを安全に制御できる.
  4. nice, renice, nohup の役割を理解し,長時間計算や重い処理を扱う際の基本的な配慮を説明できる.
  5. 実習結果を script コマンド等で記録し,プロセス・ジョブの状態遷移として報告できる.

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

マルチタスクとは

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

マルチタスクな OS で、なにができるか

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

このあとすぐ学ぶが、全体像をわかりやすく把握するために、プロセス制御、ジョブ制御で使うコマンドの違い等を下に示しておこう.

表: プロセス制御とジョブ制御の違い (詳細はこの資料内で紹介)

観点 プロセス制御 ジョブ制御
見るコマンド ps, top jobs
識別子 PID ジョブ番号
主な対象 OS が管理する実行単位 シェルが管理する作業単位
終了 kill PID kill %ジョブ番号
停止 kill -STOP PID Ctrl-Z, kill -STOP %ジョブ番号
再開 kill -CONT PID bg %番号, fg %番号
典型的な用途 暴走プロセスの確認・終了 端末上の作業の一時停止・裏実行・復帰

プロセスとは

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

プロセス制御

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

実際にそのようにプロセスの動作をその場で変えることをプロセス制御という.

プロセスの親子関係

unix では,全てのプロセスはなんらかのプロセスによって生成される. これを「親子関係」と見なして,もとのプロセスを新しいプロセスの「親プロセス」と呼ぶ.

子プロセスが終了すると,その終了状態は親プロセスが回収する.終了したにもかかわらず,親プロセスがまだ終了状態を回収していない子プロセスは,一時的にゾンビプロセスとして見える. なお,親プロセスが先に終了した子プロセスは,通常 init や systemd などに引き取られる.そのため,親が先に死んだら必ず問題になる,というわけではない.

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

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

  • オプション無しで単に ps とすると,通常は「現在の端末に関連する,自分のプロセス」が表示される. OS内の自分のプロセスすべてが表示されるとは限らないので要注意.
  • お勧めのオプションは,システム全体のプロセスがわかり,かつ, BSD でも linux でも cygwin でも有効な aux である.
    ただし,cygwin システムはほとんどプロセスを持っていないため,ps aux では寂しい結果しか表示されない.
  • cygwin では ps -W とすると,MS-Windows のプロセスも表示されるので,この方が良いかもしれない.

  ps aux とするオプションのスタイルは BSD系 Unix の ps のものだ. (BSD系でない) System V 系 Unix の影響を強く受けた Linux ではオプションに - (ハイフン) をつけるスタイルが本来で、これは異なる表記になるはずだった… が、歴史的経緯もあって、Linux の ps コマンドは両方のスタイルのオプションを広く受け付ける. 気になる人は man ps としてマニュアルを起動し、読んで確かめてみると良い.

  大学の情報教育システム, cygwin, 自分のPC上の仮想環境上の Unix等,なるべく多くの環境で ps aux を試してみよう.
また、念のため、man ps として、オンラインマニュアルを一瞥しておこう.

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

単に ps とすると…

1 PID TTY          TIME CMD
2 7229 pts/0    00:00:00 bash
3 7246 pts/0    00:00:00 ps

という感じで,「自アカウントで起動したプロセスしか見えない」が、ps aux とすると

1USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
2root           1  2.7  0.1 102800 12648 ?        Ss   20:15   0:01 /sbin/init splash
3root           2  0.0  0.0      0     0 ?        S    20:15   0:00 [kthreadd]
4root           3  0.0  0.0      0     0 ?        I<   20:15   0:00 [rcu_gp]
5…略…
6osboxes     2469  0.0  0.0 172232  4896 ?        Ssl  20:16   0:00 /usr/libexec/gvfsd-metadata
7osboxes     2515  9.7  0.8 677844 70280 ?        Dsl  20:16   0:00 /usr/libexec/gnome-terminal-se
8osboxes     2541  0.0  0.0  20616  5168 pts/0    Ss   20:16   0:00 bash
9osboxes     2550  0.0  0.0  22176  1576 pts/0    R+   20:16   0:00 ps aux

と表示され、「他人が動かしている」プロセス情報も見ることが出来る. なお上の結果は仮想環境 VirtualBox 上に OsBoxes からもってきた Ubuntu 23.10 を入れた環境のものだ.

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

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

S: sleep 状態.入力,I/O,タイマー,イベントなど,何らかの条件が満たされるのを待っている.
R: 実行中または実行可能状態.
D: 割り込み不能な待ち状態.多くの場合 I/O 待ちで,長時間続く場合は注意が必要.
T: 停止状態.Ctrl-Z や STOP シグナルなどで一時停止している.
Z: ゾンビ状態.プロセスは終了しているが,親プロセスが終了状態をまだ回収していない.

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 大きくして(= 優先順位が下がる)起動するときは

1/bin/nice hoge

とすればよい.

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

1$ ps -l
2F S   UID     PID    PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
30 S  1000    2848    2515  0  80   0 -  5154 do_wai pts/2    00:00:00 bash
40 S  1000    3501    2848  3  80   0 - 155273 do_sel pts/2   00:00:00 emacs
50 R  1000    3523    2848  0  80   0 -  5544 -      pts/2    00:00:00 ps

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

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

1renice +10 3501

とすると、

13501 (process ID) 従来の優先順位は 0 で, 新しい優先順位は 10 です

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

1$ ps -l
2F S   UID     PID    PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
30 S  1000    2848    2515  0  80   0 -  5154 do_wai pts/2    00:00:00 bash
40 S  1000    3501    2848  0  90  10 - 155273 do_sel pts/2   00:00:00 emacs
50 R  1000    3539    2848  0  80   0 -  5544 -      pts/2    00:00:00 ps

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

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

1renice -10 3501

とすると、

1renice: 3501 (process ID) の優先順位の設定に失敗しました: 許可がありません

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



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

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

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

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

1kill -シグナル pid

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

  対応するシグナル番号は環境によって異なるので、なるべくシグナル名を使おう. その環境でのシグナル番号を知るには、kill -L としてみると良い.

表: kill コマンドの主な使用例

指定例 意味
kill -TERM PID 終了を依頼する.kill PID とほぼ同じ.
kill -KILL PID 強制終了する.最後の手段.
kill -INT PID Ctrl-C 相当の割り込み.
kill -TSTP PID Ctrl-Z 相当の一時停止.
kill -STOP PID 強制的に一時停止.
kill -CONT PID 停止中のプロセスを再開.
kill -HUP PID 端末切断相当.デーモンでは設定再読込に使われることもある.

  kill コマンドの使い分けを念頭に置くと,プロセスを止めたいという時は基本的に以下のような手順になるだろう.

  1. まず Ctrl-C を試す.
  2. 別端末から kill PID,つまり TERM を送る.
  3. 少し待って ps で確認する.
  4. それでも終了しない場合に限り,最後の手段として kill -KILL PID を使う.

シグナル "HUP" や PID "1" "0" "-1" は特別な意味で使われることが多いので,これらを扱うのは危険であると認識しよう.

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

カーネギーメロン大学卒,MIT で修士号,スタンフォード大学で博士号をとって今は Google 勤めの Dan Maynes-Aminzade による ラップ曲 Kill -9 というのがある.ああ~,と生暖かい目で一度見ておくといいだろう.



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

nohup は「指定したコマンドが HUP シグナル(= 端末切断、ログアウト)で終了しにくいようにして起動する」ためのコマンドである. これにより、ユーザがログアウトしても動き続けるようにプログラムを起動することが出来る.

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

主に、Bshell系の場合は次のようにして用いる. なお、最後の & によりバックグラウンドで起動し、標準出力・標準エラー出力は output.log に保存するように指定している.

1nohup 長時間かかるコマンド > output.log 2>&1 &

ちなみに、fish の場合は以下の通り. 最後の disown は、投入したコマンドをシェルの管理から切り離すコマンドである.これをつけなくても多くの場合で大丈夫だと思うが、念の為.

1nohup 長時間かかるコマンド &> output.log &; disown

コマンド指定無しで単に nohup と起動しないこと! 最近の環境では問題ないが、古めの環境ではそれ以降のコマンド全てが nohup 扱いになりログアウトしても起動したプログラムの動作が止まらないことがある.大きなトラブルを引き起こす可能性が高い.

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

  1. 端末エミュレータ中で emacs -nw と入力して emacs を起動する.
      emacs がインストールされていない場合は、ここでインストールしておこう. インストール方法は脚注1を見よう.
  2. emacs が起動している状態で CTRL-z として,emacs を停止状態にする.
      CTRL-z は、実行中のプログラムを一時停止してシェルに戻るためのキーである. 詳しいジョブ制御としての意味は後で説明する.
  3. ps コマンドを試してみて,emacs の存在とその状態を把握する.
  4. top コマンドを試してみて,emacs の存在とその状態を把握する.
  5. ps コマンドで emacs の PID を把握し,kill コマンドを用いて emacs を強制終了させる.
  6. psコマンドで emacs が存在しない(強制終了したため)ことを確認する.



ジョブとは

ジョブとは,シェルが一つの作業として管理する単位である. 多くの場合,ユーザが入力した一つのコマンド行に対応するが,パイプラインのように複数のプロセスからなることもある. ジョブは jobs コマンド(後述する)で確認でき,%1, %2 のようなジョブ番号で指定する.

まあ簡単に言えば,シェルで命令した「(一回分の)命令の固まり」であり,人間にとって一つの処理と見なせるもの,と思えばよい.

フォアグラウンド状態,バックグラウンド状態、一時停止状態

シェルは人間と「対話的」に動作するため,そのまま複数のプログラムを同時に動作させるわけにはいかない.

そこで,「人間と対話しながら作業するジョブを一つだけ」と 「そうでないジョブ(複数)」 をわけることでマルチタスクを可能としている.

その全体の関係は下の図のようになっている.

job status

図: ユーザとジョブと、3つの状態

それぞれの状態の説明は以下の通り.

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

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

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

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

1osboxes@osboxes:~$ jobs
2[1]-  停止                  emacs -nw
3[2]   実行中               sleep 1000 &
4[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 コマンド

以下のようにして見えてないジョブを、動かしたり停めたり出来る

コマンド 説明
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 コマンドを用いて編集したファイルを見て,先ほどの日時書き込みが保存されていることを確認(''ジョブの投入 & 停止'').

レポート No.3

  注意

近年はセキュリティ上の懸念から,実行形式のプログラムなどをメールに添付するとそのメールそのものの受信を受信側サーバが拒絶したりする. そういうことを避けるため,レポートをファイルで提出するときはそういった懸念のあるファイル形式のものではないようにしよう.

まあ要するに,レポートは pdf ファイルにして送るのが良い ということだと思っておこう.

以下の課題について,なるべく一生懸命な調査と考察を行って,
     学籍番号-氏名-03.pdf
というファイルとしてレポートを作成し、 webフォーム から教官宛に提出しよう.

なお,レポートを $\TeX$ 等で作成したものを印刷した「紙媒体」を教官に直接手渡す形で提出してもよいが、物質によるレポート提出は常に破損や紛失の可能性があるのであまりお勧めはしないぞ.

  1. 本資料の実習部分をすべて行い,それについて報告せよ.
    なお、レポートには以下の内容を含めよう.
  • 実行したコマンド列
  • そのとき観察された ps, top, jobs の出力
  • 各ジョブ・プロセスがどの状態にあったかの説明, 自分が行った状態遷移
      : foreground → stopped → background → foreground → terminated
  • うまくいかなかった操作があれば、その原因の考察

  1. ジョブを投入して停止状態にする方法が複数あるとされる.これを調べて報告せよ.
    また,これらの作業を実際にやってみよ. なお,この操作の様子を script コマンドで記録しておいて,その記録を提出する形で報告すること.
    script は以下のようにして使うと, script コマンドを起動してから exit と入力するまでの入力操作と出力がすべてファイルに記録できる便利なコマンドだ.
1script log.txt
2ここで作業を行う
3exit

なお、上の log.txt というファイル名は何でも良い.このファイルに下記の作業の記録が残る.

3. ジョブをあわせて 3つほど投入して共存させ、三状態を自在に遷移させてみよう.
なお,この操作の様子も script コマンドで記録し,その記録をあわせて提出しよう.

4. kill でもプロセスを止められない場合が何通りかあるとされる. これを調べて、報告せよ.


  1. emacs をインストールするには,Ubuntu の場合は端末エミュレータ上で
    sudo apt install emacs
    とコマンドを打てば良い.パスワードを要求されたらきちんと入れよう.なおこの途中に "メールサーバソフト postfix の設定はどうする?" という選択肢が出ることがある.これは emacs がメールを読み書きできる機能を持つことから「ついでに postfix をインストールする?」ということで尋ねてきているのだ.通常は特に不要だろうから,"unchanged" を選べば良い. ↩︎