ジョブ制御(Unix)
「ジョブ」はプロセスグループのためのシェルの表現であり、UnixやUnixライクなオペレーティングシステムでは、 ジョブ制御は 、特に対話形式で、シェルによってジョブの制御を指します。基本的なジョブ制御機能は、ジョブ/プロセスグループ内のすべてのプロセスの中断、再開、または終了です。ジョブにシグナルを送信することにより、より高度な機能を実行できます。ジョブ制御は、そのマルチプロセッシングのためにUnixで特に関心があり、一般にジョブ制御と区別されるべきです。ジョブ制御は、順次実行(バッチ処理)に頻繁に適用されます。
概要
ターミナル(またはターミナルエミュレータ)を介してUnixまたはUnixライクなオペレーティングシステムを使用する場合、ユーザーは最初は単一のプロセス、つまりログインシェルのみを実行します。ほとんどのタスク(ディレクトリリスト、ファイルの編集など)は、プログラムに端末の制御を任せ、プログラムの終了時に制御をシェルに戻すことで簡単に実行できます。これは、ターミナルから読み取りまたは書き込みを行い、Ctrl + Cを押した結果生じる終了信号のように、キーボードから送信された信号をキャッチします。
ただし、ユーザーが端末を別の目的で使用しているときにタスクを実行したい場合があります。実行されているが端末からの入力を受け取っていないタスクは「バックグラウンドで」実行され、端末からの入力を受け取っている単一のタスクは「フォアグラウンドで」実行されていると言われます。ジョブ制御は、これを可能にするために開発された機能です。ユーザーがバックグラウンドでプロセスを開始し、すでに実行中のプロセスをバックグラウンドに送信し、バックグラウンドプロセスをフォアグラウンドに持ち込んで、プロセスを一時停止または終了できます。
ジョブの概念は、単一のシェルコマンドの(シェル)概念を、コマンドが伴う可能性のある多くのプロセスの(オペレーティングシステム)概念にマップします。プロセスが追加の子プロセスを作成し、単一のシェルコマンドが複数の通信プロセスのパイプラインで構成される場合があるため、マルチプロセスタスクが発生します。たとえば、テキスト「title」を含む行を選択し、これらをアルファベット順にソートし、ページャーに結果を表示するコマンド。
これにより、少なくとも3つのプロセスが作成されます。1つはgrep、1つはソート、もう1つはそれ以下です。ジョブ制御により、シェルはこれらの関連プロセスを1つのエンティティとして制御でき、ユーザーが適切なキーの組み合わせ(通常はControl + Z)を発行すると、プロセスのグループ全体が一時停止します。
ジョブはオペレーティングシステムによって単一のプロセスグループとして管理され、ジョブはそのようなグループのシェルの内部表現です。これはPOSIXで次のように定義されています。
シェルパイプラインを構成する一連のプロセスと、そのパイプラインから派生したすべてのプロセスは、すべて同じプロセスグループに属します。
ジョブは、 ジョブ制御ジョブIDまたは単にジョブIDと呼ばれるハンドルによって参照できます 。これは、ジョブを参照するためにシェル組み込みコマンドによって使用されます。ジョブIDは%文字で始まります。 %nはジョブnを識別しますが、%%は現在のジョブを識別します。他のジョブIDはPOSIXによって指定されます。非公式の使用法では、この番号は「ジョブ番号」または「ジョブID」と呼ばれることがあり、Bashのドキュメントでは(%接頭辞付きの)ジョブIDがjobspecとして参照されます。
ジョブ制御とジョブIDは通常、対話型でのみ使用され、プロセスグループの参照を簡素化します。スクリプトではPGIDが代わりに使用されます。PGIDはより正確で堅牢であり、実際、bashスクリプトではジョブ制御がデフォルトで無効になっているためです。
歴史
ジョブ制御は、最初にCシェルでJim Kulpによって実装され、次にオーストリアのIIASAで4.1BSDカーネルの機能を利用して実装されました。ベル研究所で開発されたKornシェルはそれを採用し、後にBourneシェルのSVR4バージョンに組み込まれ、ほとんどの最新のUnixシェルに存在します。
コマンド
POSIX標準は、バックグラウンドとフォアグラウンド(それぞれbgとfg)で中断されたジョブを再開するための2つのコマンドを指定しています。これらは、Kornシェルのジョブ制御コマンドをモデルにしています。
実装
通常、シェルはジョブのリストをジョブテーブルに保持します 。ジョブは、パイプラインのすべてのメンバーとその子孫で構成されるプロセスグループに対応することを思い出してください。 jobsコマンドは、ジョブテーブルに存在するバックグラウンドジョブを、それらのジョブ番号とジョブ状態(停止または実行中)と共にリストします。ユーザーがログアウトするとセッションが終了すると(シェルを終了し、 セッションリーダープロセスが終了します)、シェルプロセスはすべてのジョブにSIGHUPを送信し、プロセスグループが終了するのを待ってから終了します。
disownコマンドは、ジョブテーブルからジョブを削除するために使用できます。これにより、セッションが終了したときに、子プロセスグループはSIGHUPを送信せず、シェルはそれらが終了するのを待ちません。したがって、これらは孤立したプロセスになり、オペレーティングシステムによって終了される可能性がありますが、より頻繁に使用されるため、プロセスはinitによって採用され(カーネルは親プロセスをinitに設定)、デーモンとして実行を続けます。ジョブが終了しないようにする代替手段には、nohupとターミナルマルチプレクサーの使用が含まれます。
一時停止文字(Ctrl-Z)を入力すると、フォアグラウンドで実行中のジョブを停止できます。これにより、「ターミナル停止」信号( SIGTSTP )がプロセスグループに送信されます。デフォルトでは、SIGTSTPはそれを受信するプロセスを停止させ、制御がシェルに返されます。ただし、プロセスはSIGTSTPのシグナルハンドラを登録したり、無視したりできます。プロセスは、「停止」シグナル( SIGSTOP )で一時停止することもできますが、これはキャッチも無視もできません。
中断文字(Ctrl-C)を入力すると、フォアグラウンドで実行中のジョブを中断できます。これにより、「割り込み」シグナル( SIGINT )が送信されます。このシグナルは、デフォルトでプロセスを終了しますが、オーバーライドできます。
停止したジョブは、bg組み込みのバックグラウンドジョブとして、またはfgのフォアグラウンドジョブとして再開できます。どちらの場合でも、シェルはI / Oを適切にリダイレクトし、 SIGCONTシグナルをプロセスに送信します。これにより、オペレーティングシステムは実行を再開します。 Bashでは、コマンドラインにアンパサンド(&)を追加することにより、プログラムをバックグラウンドジョブとして開始できます。その出力は端末に向けられます(他のプログラムの出力とインターリーブされる可能性があります)が、端末の入力から読み取ることはできません。
制御端末からの読み取りまたは書き込みを試みるバックグラウンドプロセスには、 SIGTTIN (入力用)またはSIGTTOU (出力用)信号が送信されます。これらのシグナルはデフォルトでプロセスを停止しますが、他の方法で処理することもできます。シェルは多くの場合、SIGTTOUのデフォルトの停止アクションをオーバーライドして、バックグラウンドプロセスがデフォルトで制御端末に出力を配信するようにします。
Bash互換シェルでは、killビルトイン(/ bin / killではない)は、ジョブIDおよびプロセスグループIDによってジョブにシグナルを送ることができます。ジョブにシグナルを送信すると、プロセスグループ全体に送信されます。 IDは、「%」をプレフィックスとして削除する必要があります。 Killは任意のシグナルをジョブに送信できますが、プロセスのシステムを削除することを目的とする場合、シグナルSIGKILLおよびSIGTERM(デフォルト)がおそらく最も適切です。