ご存知のように、シェルを使用すると、ユーザーはコマンドラインの最後で&
を使用してバックグラウンドプロセスを実行できます。各バックグラウンドプロセスは、ジョブIDによって、そしてもちろん、そのPIDによって識別されます。
新しいジョブを実行すると、出力は[1] 1234
のようになります(2番目の数字はプロセスIDです)。 stop 1
やstop %1
などのコマンドを呼び出そうとすると、失敗メッセージが表示されます:stop: Unknown job: 1
stop
コマンドが原因でジョブが一時停止されることを理解して、私はジョブIDを取得する方法と思って正しく実行しました。ジョブを強制終了する唯一の方法がそのプロセスIDによる場合、ジョブIDの目的は何ですか?
プロセスが&
でバックグラウンドに送信された後、そのPIDは変数$!
から取得できます。ジョブIDはjobs
コマンドを使用して表示できます。-l
スイッチはPIDも表示します。
$ sleep 42 &
[1] 5260
$ echo $!
5260
$ jobs -l
[1] - 5260 running sleep 42
一部のkill
実装では、PIDではなくジョブIDで強制終了できます。しかし、ジョブIDのより賢明な使用法は、特定のプロセスを選択的にフォアグラウンドにすることです。 5つのプロセスをバックグラウンドで開始し、3番目のプロセスをフォアグラウンドにしたい場合は、jobs
コマンドを実行して起動したプロセスを確認し、次にfg %3
を実行してジョブID 3のプロセスをフォアグラウンドにできます。
ジョブ制御は、80年代前半にBSDシステムに追加された機能です。 csh
はBSDシェルなので、最初にそのシェルで機能が導入されたのは当然のことです。ジョブ制御が非BSD Unicesおよび他のシェル(Kornシェル以降)に追加されるまで、数年待つ必要がありました。
ジョブ制御はプロセスを非同期で開始する機能ではないことに注意してください(つまり、シェルは次のプロンプトを発行する前に終了を待機していません。ボーンシェルには最初から_&
_がありましたが、そうではありませんでした。 10年後まではジョブ制御をサポートします)が、同じ端末から複数のタスクを操作する機能についてです。
jobsのことであり、processesではありません。ジョブ制御を実装するために、プロセスグループが導入されました。ジョブはプロセスグループです(ジョブユーザーの観点から、プロセスグループ OS実装の観点から)。
これは通常、プロセスグループを作成および管理する対話型シェルであり、繰り返しになりますが、端末との対話がすべてです。
コマンドを実行すると、すべてのプロセスとその子孫が新しいプロセスグループに配置されます。
特定の端末でのセッションでは、1つのプロセスグループに端末への特権アクセスが許可されます。これがターミナルのフォアグラウンドプロセスグループです。そのプロセスグループは、押すとメンバーがSIGINT/SIGTSTP/SIGQUITを受け取るグループです。 CTRL-{C,Z,\}。他のプロセスグループ内のプロセスは、端末からの読み取り(および書き込み)を試みると中断されます。等.
フォアグラウンドでジョブを開始すると、インタラクティブシェルはそのプロセスグループをターミナルのフォアグラウンドプロセスグループにし、バックグラウンドで開始されたプロセスグループにはしません。
このようにして、ユーザーは端末で一度にいくつかのことを行うことができ、ジョブのリストを取得できます。ジョブのリストは、(端末への特権アクセスで)フォアグラウンド、バックグラウンド、実行中、または一時停止にできるプロセスのグループです。また、ジョブをバックグラウンド/フォアグラウンドから/に移動したり、一時停止/実行したりできます。
現在、すべてのシステムとすべてのシェルがジョブ制御をサポートしています(ただし、Bourneシェルでは、_set -m
_ IIRCで明示的に要求する必要があります)。
プロンプトで開始されたすべてのコマンドには、ジョブ番号が与えられます。システムの観点からは、これらはプロセスグループIDにマップされます。
_ $ ps -fj | cat
UID PID PPID PGID SID C STIME TTY TIME CMD
chazelas 8237 7315 8237 8237 0 15:54 pts/7 00:00:00 /bin/zsh
chazelas 10720 8237 10720 8237 0 21:25 pts/7 00:00:00 ps -fj
chazelas 10721 8237 10720 8237 0 21:25 pts/7 00:00:00 cat
_
上記では、その_ps -j | cat
_ジョブをフォアグラウンドで開始しました。 10720プロセスグループが割り当てられました(これは、ps
のプロセスIDでもあります)。 (tcgetpgrp()
を使用して)実行しているときにターミナルのフォアグラウンドプロセスグループIDを照会した場合、10720になります。
_$ (sleep 100;:) | sleep 101 &
[1] 10787 10789
$ ps -fj
UID PID PPID PGID SID C STIME TTY TIME CMD
chazelas 8237 7315 8237 8237 0 15:54 pts/7 00:00:00 /bin/zsh
chazelas 10787 8237 10787 8237 0 21:33 pts/7 00:00:00 /bin/zsh
chazelas 10788 10787 10787 8237 0 21:33 pts/7 00:00:00 sleep 100
chazelas 10789 8237 10787 8237 0 21:33 pts/7 00:00:00 sleep 101
chazelas 10790 8237 10790 8237 0 21:33 pts/7 00:00:00 ps -fj
_
そのとき、私はバックグラウンドでパイプラインを開始しました。シェルはジョブ番号(1)と2つのプロセスID(_(t)csh
_、pdksh
およびzsh
はすべてのプロセスIDを提供し、動作は他のシェルで異なります)、サブシェル用、_sleep 101
_用。ちなみに、そのサブシェルは3番目のプロセス(_sleep 100
_)を生成しました。シェルはそれについて何も知らないため、そのpidを報告しませんでしたが、それでも同じジョブの一部です(上記のプロセスグループ10787)。
これで、これらのジョブを操作するためのコマンドがいくつかあります:fg
、bg
、kill
、wait
、jobs
(一部のシェルには、disown
と、kill
へのエイリアスもあり、別のデフォルトシグナルを送信します)。
これらは_%n
_の形式でジョブIDを取得します。ここで、n
はジョブ番号です。コマンドがfoo
で始まるジョブを参照する_%foo
_などがあります(シェルのマニュアルを参照)。
_kill %1
_
sIGTERMシグナルをジョブ1のすべてのプロセスに送信します。_kill -s STOP %1
_は、stop
にエイリアスされている場合があり、代わりにSIGSTOP
シグナルを送信します。
_fg %1
_
これをフォアグラウンドに置きます(端末のフォアグラウンドプロセスグループにし、SIGCONTシグナルを送信して、シェルに完了を待つように指示します)。
_bg %1
_
停止したバックグラウンドジョブを再開(SIGCONTを送信)します。
_wait %1
_
ジョブはフォアグラウンドに置かれませんが、その完了を(または中断されるまで)待機します。
_jobs
_
現在のジョブのリスト(_jobs -l
_ pgidsおよび/またはpids)。
_%%
_および_%+
_はcurrentジョブを参照します。現在のジョブは、必ずしも最後に開始されたジョブであるとは限らないことに注意してください。これは、jobs
'の出力で_+
_でマークされたジョブです。引数なしのfg
またはbg
が処理するのもジョブです。中断されているジョブがある場合は、最後に中断されたジョブです。すべてが実行されている場合は、最後に開始されたジョブです。
したがって、中断されたジョブがある場合、_kill %%
_はバックグラウンドで開始したばかりのジョブを強制終了しないことに注意してください。 jobs
を実行して、現在実行中のジョブを確認してから、ジョブを強制終了することをお勧めします。
ジョブを処理するときはnotプロセスIDで使用する必要があることに注意してください。
_$!
_は、バックグラウンドで実行された最後のコマンドのプロセスIDを返します。パイプラインの場合、それは右端のコマンドのPIDです。
_$ sleep 100 | sleep 101 & ps -fj; echo "$!"
[1] 11044 11045
UID PID PPID PGID SID C STIME TTY TIME CMD
chazelas 8237 7315 8237 8237 0 15:54 pts/7 00:00:00 /bin/zsh
chazelas 11044 8237 11044 8237 0 22:08 pts/7 00:00:00 sleep 100
chazelas 11045 8237 11044 8237 0 22:08 pts/7 00:00:00 sleep 101
chazelas 11046 8237 11046 8237 0 22:08 pts/7 00:00:00 ps -fj
11045
_
$以上!は11045ですが、ジョブ1のプロセスグループは11044です。_kill "$1"
_の場合、_sleep 101
_ではなく_sleep 100
_のみを強制終了します。私が_kill -- "-$!"
_(つまり、プロセスグループ11045内のすべてのプロセスを強制終了する)の場合、そのようなプロセスグループがないため、失敗します。
そこで、_kill %1
_(または_kill %sleep
_、または_kill %%
_)を使用してそのジョブを終了する必要があります(これにより_kill -- -11044
_が実行されます)。
stop
と入力すると、実際にはupstart
に付属しているコマンドを実行していたようです。これは、使い慣れたcsh
またはksh
組み込みstop
コマンドとは関係ありません。したがって、「不明なジョブ」に関するエラーメッセージは、実行しようとしていたことには当てはまりません。
ksh
は、stop
をkill -s STOP
のエイリアスとして実装します。これは、ジョブまたはプロセスグループIDの%n
表記を理解します。このエイリアスは他のシェルでも機能します。たとえば、シェルがbash
の場合:
$ alias stop='kill -s STOP'
$ sleep 30&
[3] 9820
$ stop %3
[3]+ Stopped sleep 30