『UNIX今日の技』での記事をまとめたものです。

1 フォアグラウンドとバックグラウンド

シェルからコマンドを投げる時、通常はそのコマンドの処理が終わるまでユーザ入力ができなくなります。 このように実行されるジョブをフォアグラウンドジョブと呼びます。 例えば、こんな感じ。 sleep 10

「ずーっと制御が返って来ないというのもかったるいなー」という場合にはジョブをバックグラウンドで投げたら良いです。 バックグラウンドジョブとして投げるには、コマンドの最後に & をつけます。 sleep 10 &

この「&」はコマンドの区切り「;」と同じような性格を持っています。 両者の違いは、コマンドを「フォアグラウンドで実行する」か、「バックグラウンドで実行する」か、という点です。 sleep 10 ; ls & ではフォアグラウンドで 10秒寝てから、バックグラウンドで ls を実行します。 sleep 10 & ls ; (コマンドラインの最後の「;」は省略できます) ではバックグラウンドで10秒寝るジョブを走らせながら、同時にフォアグラウンドで ls を実行します。

2 サスペンド

フォアグラウンドとバックグラウンドについては既に説明しました。 これらは何れもコマンドが実行されていて、その実行されている状態が異なります。 もう少し正確に言うならば、「端末を占有しているか否か」という点が異なるということになります。

さて、プログラムは常に実行されているとは限りません。 ユーザの都合やその他の理由で「停止」させられることがあります。 この状態を「サスペンド」と呼びます。

よくある場面として、フォアグラウンドで実行してしまったウィンドウプログラムをバックグラウンドに移す為に一時的にそれをサスペンドする、というのが挙げられます。

他の場面としては、バックグラウンドジョブでキーボード入力が要求された場合、シェルはそのジョブをサスペンドします。 (ただし、 「command < input.txt &」のように入力をリダイレクトしていれば、input.txt からの入力を用いてコマンドを続行します。)

以上挙げた 3つのジョブの状態、 * フォアグラウンド * バックグラウンド * サスペンド を上手に管理できるとそれなりに便利です。

3 ジョブとプロセス

実際のジョブの操作に入る前に、ジョブがどういう物なのかをシステム面からちょっと見てみましょう。

UNIX で用いられるタスク関連の用語として「ジョブ」と「プロセス」があります。 これらの意味の違いを明確に把握しているでしょうか?

「ジョブ」というのはシェルが管理する仕事の単位です。 シェルは自分が起動したコマンド一つ一つを「ジョブ」という単位として扱い、そしてジョブの状態を常に把握、管理します。

一方「プロセス」は、OS によって一元管理される仕事の単位です。 OS は現時点で生きている全てのプロセスの状態を把握し、管理しています。

則ち、一つのコマンドをシェルから実行した場合、その仕事は「ジョブ」としても「プロセス」としても操作することができる、ということです。 ただし、同じホストにログインしている別のシェルからは「プロセス」としてしか操作できません。

要するに、仕事の実体は一つだけど、「ジョブはシェルによって」「プロセスは OS によって」と管理される主体が違う、ということです。

あ、そうそう、「タスク」という言葉もありますが、これは仕事を抽象的に表現する言葉であまり具体的な意味はないと思います。

4 ジョブの確認

現在のジョブの状態は、「jobs」コマンドで確認できます。 * 「running」 はバックグラウンドジョブ * 「suspended」はサスペンド中のジョブ 「あれ、フォアグラウンドジョブは?」 フォアグラウンドで何か走っていたら、そもそも jobs コマンドを実行できませんね。

てゆーか、その時点では jobs コマンドがフォアグラウンドジョブです。

5 ジョブを中止する

kill コマンドを使います。 kill コマンドの引数にはプロセス ID と ジョブ番号のどちらでも指定できます。

ippei@odin % ps | grep sleep
51981  p4  IN     0:00.00 sleep 100
51984  p4  IN     0:00.00 sleep 200
ippei@odin %  kill 51981

ippei@odin % jobs
[1]  - running    sleep 100
[2]  + running    sleep 200
ippei@odin % kill %1

ジョブ番号の場合は番号の直前に 「%」を付けます。

:zshのPID補完:「kill sle[TAB]」のようにコマンド名 sleep の一部を入れて TAB キーを押すと、コマンド名にマッチするプロセスの PID を補完してくれたりします。

5.1 フォアグラウンドジョブを中止する(C-c)

おそらく言う間でもないことですが、フォアグラウンドジョブの場合は [C-c] で終了できます。

6 バックグラウンド/フォアグラウンドに移動させる

ジョブをフォアグラウンドに移したい場合は「fg」、 バックグラウンドに移したい場合は「bg」でいけます。

ippei@odin % jobs
[1]  - running    sleep 100
[2]  + running    sleep 200
ippei@odin % fg %1

ippei@odin % jobs
[1]  - running    sleep 100
[2]  + suspended  sleep 200
ippei@odin % bg %2

なお、引数を省略した場合は最後に操作したジョブ(「+」マークがついているジョブ)が対象になります。 ジョブが一つしかない場合などは「bg」だけで操作するのがラクチンです。

7 ジョブをサスペンドする

7.1 フォアグラウンドジョブをサスペンド

大抵の端末で [C-z] にバインドされています。

7.2 プロセスをサスペンド

kill -TSTP <PID>

を使えば、バックグラウンドジョブだろうが、他のシェルのフォアグラウンドジョブだろうがサスペンド状態にすることができます。

8 ジョブの状態遷移図

以上のジョブ操作をまとめると以下の図のようになります。 * フォアグラウンドにする場合は「fg」 * バックグラウンドにする場合は「bg」 * サスペンドにする場合は [C-z] * 殺す場合は「C-c」or「kill」

{{ref_image jobs.png}}

9 細かい話:シェルへの依存性

「ジョブはシェルによって管理される」ということは、シェルによってジョブの操作方法が違うことがある、ということを意味します。 まあ大抵は同様の操作方法で行けるように設計されていますが。

例えば、「バックグラウンドジョブをサスペンド」という操作は「kill -TSTP」だと紹介しましたが、tcsh では内部コマンド「stop」が用意されています。 zsh にはそれっぽいコマンドは用意されていないようです。 (まあ、「alias stop="kill -TSTP"」でほぼ等価だと思いますが)。

10 バックグラウンドジョブの終了を監視する(wait コマンド)

wait はバックグラウンドジョブが終了するのを監視し、対象のジョブが終了するまで制御を返しません。

ippei@odin % sleep 10 &
[1] 97664

ippei@odin % jobs
[1]  + running    sleep 10

この状態で「wait; sleep 20」とすると、先に打ち込んだ「sleep 10」が終了し次第、次の「sleep 20」を実行します。 複数のバックグラウンドジョブがある場合は「wait %2」のように指定します。

バックグラウンドで走らせている計算が終わり次第次の計算を投げる、という応用が考えられます。 ippei@odin % jobs [1] + running vasp

ippei@odin % wait ; vasp &

11 kill

kill コマンドはプロセスやジョブを終了させるために使います。 普通の人はこう思っておけば良いです ……ということで以下は普通でない人向け。

kill コマンドは「プロセスを終了させる」のではなく、より厳密には「プロセスにシグナルを送る」為のコマンドです。 ただ、kill コマンドのデフォルトが「終了要求(TERMシグナル)」である、というだけの事です。

kill -TERM 2343

のようにコマンドを打つと、 PID(プロセスID) が 2343 のプロセスに TERM シグナルを送ります。 通常のプロセスは、TERMシグナルを受け取ると自分のプロセスを終了させようとします。

12 終了要求

kill <PID>

のようにシグナル名を省略するとデフォルトシグナルである TERM シグナルを送ります。 TERM シグナルは「通常の終了要求」であり、大抵のプロセスはこれで止まります。

13 強制終了要求

TERM シグナルで止まらないプロセスは「強制終了」で止める必要があります。 kill -KILL KILL シグナルのシグナル番号は 9 番なので、 kill -9 でも同じことになります。

14 一時停止要求(サスペンド)

プロセスを一時的に止めたい場合に使います。 プロセスは C-z したようなサスペンド状態に移行します。 kill -TSTP

15 プロセスの再開要求

サスペンド状態のプロセスを再開させます。 kill -CONT

サスペンドと再開を使って、MedeA で投げた vasp プロセスを操作できるのではないかと考えています。 まだ試していませんが。

16 他のシグナル

他にも幾つかあるらしいけど、私は使ったことがないです。 man kill すれば書いてあるので興味のある方はそちらでどうぞ。