Bashのジョブコントロールを使用して前景および背景プロセスを管理する方法

前書き

このガイドでは、bash、Linuxシステム、および端末がどのように連携してプロセスとジョブ制御を提供するかについて説明します。 previous guideでは、pskill、およびniceコマンドを使用してシステム上のプロセスを制御する方法について説明しました。

この記事では、フォアグラウンドプロセスとバックグラウンドプロセスの管理に焦点を当て、シェルのジョブ制御機能を活用してコマンドの実行方法の柔軟性を高める方法を示します。

前景プロセスの管理

Linuxマシンで開始するほとんどのプロセスはフォアグラウンドで実行されます。 このコマンドは実行を開始し、プロセスの間シェルの使用をブロックします。 プロセスは、ユーザーとの対話を許可する場合もあれば、単にプロシージャを実行して終了する場合もあります。 デフォルトでは、出力は端末ウィンドウに表示されます。 フォアグラウンドプロセスを管理する基本的な方法を以下で説明します。

プロセスを開始する

デフォルトでは、プロセスはフォアグラウンドで開始されます。 プログラムが終了するか状態が変わるまで、シェルと対話することはできません。

一部のフォアグラウンドコマンドは非常に迅速に終了し、すぐにシェルプロンプトに戻ります。 例えば、このコマンドは:

echo "Hello World"

これにより、「Hello World」が端末に出力され、コマンドプロンプトに戻ります。

他のフォアグラウンドコマンドは実行に時間がかかり、その間シェルアクセスをブロックします。 これは、コマンドがより広範な操作を実行しているため、または明示的に停止されるまで、または他のユーザー入力を受け取るまで実行するように構成されているためです。

無期限に実行されるコマンドは、topユーティリティです。 開始後、ユーザーがプロセスを終了するまで実行を継続し、表示を更新します。

top

「q」と入力して終了できます。 一部のプロセスには専用の終了機能がありません。 それらを停止するには、別の方法を使用する必要があります。

プロセスの終了

コマンドラインで単純なbashループを開始するとします。 10秒ごとに「Hello World」を出力するループを開始できます。 このループは、明示的に終了するまで永遠に続きます。

while true; do echo "Hello World"; sleep 10; done

ループには「終了」キーはありません。 signalを送信してプロセスを停止する必要があります。 Linuxでは、カーネルはプロセスのシグナルを送信して、プロセスの終了または状態の変更を要求できます。 Linux端末は通常、CTRL-Cキーの組み合わせが押されたときに、「SIGINT」信号(通常は信号番号2)を現在のフォアグラウンドプロセスに送信するように構成されています。 SIGINTシグナルは、ユーザーがキーボードを使用して終了を要求したことをプログラムに伝えます。

開始したループを停止するには、コントロールキーを押しながら「c」キーを押します。

CTRL-C

ループが終了し、制御がシェルに戻ります。

CTRL-Cの組み合わせによって送信されるSIGINTシグナルは、プログラムに送信できる多くのシグナルの1つです。 ほとんどの信号にはキーボードの組み合わせが関連付けられていないため、代わりにkillコマンドを使用して送信する必要があります(これについては後で説明します)。

プロセスの一時停止

前に述べたように、フォアグラウンドプロセスは実行中はシェルへのアクセスをブロックします。 フォアグラウンドでプロセスを開始した後、ターミナルにアクセスする必要があることに気付いた場合はどうなりますか?

送信できるもう1つの信号は、「SIGTSTP」信号(通常、信号番号20)です。 CTRL-Zを押すと、端末は「サスペンド」コマンドを登録します。このコマンドは、SIGTSTP信号をフォアグラウンドプロセスに送信します。 これは基本的にコマンドの実行を一時停止し、制御を端末に返します。

実例を示すために、pingを使用して5秒ごとにgoogle.comに接続してみましょう。 pingコマンドの前にcommandを付けます。これにより、コマンドに最大カウントを人為的に設定するシェルエイリアスをバイパスできます。

command ping -i 5 google.com

コマンドをCTRL-Cで終了する代わりに、代わりにCTRL-Zと入力します。

CTRL-Z

次のような出力が表示されます。

Output[1]+  Stopped                 ping -i 5 google.com

pingコマンドが一時的に停止され、シェルプロンプトに再びアクセスできるようになりました。 psプロセスツールを使用して、これを示すことができます。

ps T
Output  PID TTY      STAT   TIME COMMAND
26904 pts/3    Ss     0:00 /bin/bash
29633 pts/3    T      0:00 ping -i 5 google.com
29643 pts/3    R+     0:00 ps t

pingプロセスはまだリストされていますが、「STAT」列には「T」が含まれていることがわかります。 psのマニュアルページは、これが「(a)ジョブ制御信号によって停止された」ジョブを表していることを示しています。

プロセスの状態を変更する方法について詳しく説明しますが、今のところ、次のように入力して、フォアグラウンドでコマンドの実行を再開できます。

fg

プロセスが再開したら、CTRL-Cで終了します。

CTRL-C

バックグラウンドプロセスの管理

フォアグラウンドでプロセスを実行する主な代替方法は、バックグラウンドで実行できるようにすることです。 バックグラウンドプロセスは、それを開始した特定の端末に関連付けられていますが、シェルへのアクセスをブロックしません。 代わりに、バックグラウンドで実行され、コマンドの実行中にユーザーがシステムと対話できるようにします。

フォアグラウンドプロセスがそのターミナルと対話する方法のために、すべてのターミナルウィンドウに対して1つのフォアグラウンドプロセスしか存在できません。 バックグラウンドプロセスは、プロセスの完了を待たずにすぐに制御をシェルに返すため、多くのバックグラウンドプロセスを同時に実行できます。

開始プロセス

コマンドの最後にアンパサンド文字(「&」)を追加することにより、バックグラウンドプロセスを開始できます。 これにより、プロセスが完了するのを待たずに、実行を開始してすぐにユーザーをプロンプトに戻すようシェルに指示します。 コマンドの出力は(redirectedを除いて)引き続きターミナルに表示されますが、バックグラウンドプロセスの続行中に追加のコマンドを入力できます。

たとえば、次のように入力して、バックグラウンドの最後のセクションから同じpingプロセスを開始できます。

command ping -i 5 google.com &

次のようなbashジョブ制御システムからの出力が表示されます。

Output[1] 4287

pingコマンドからの通常の出力も表示されます。

OutputPING google.com (74.125.226.71) 56(84) bytes of data.
64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=1 ttl=55 time=12.3 ms
64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=2 ttl=55 time=11.1 ms
64 bytes from lga15s44-in-f7.1e100.net (74.125.226.71): icmp_seq=3 ttl=55 time=9.98 ms

ただし、コマンドを同時に入力することもできます。 バックグラウンドプロセスの出力は、フォアグラウンドプロセスの入力と出力に混在しますが、フォアグラウンドプロセスの実行を妨げることはありません。

バックグラウンドプロセスのリスト

停止またはバックグラウンドで実行されたすべてのプロセスを表示するには、jobsコマンドを使用できます。

jobs

pingコマンドをバックグラウンドで実行している場合は、次のようなものが表示されます。

Output[1]+  Running                 command ping -i 5 google.com &

これは、現在1つのバックグラウンドプロセスが実行されていることを示しています。 [1]は、コマンドの「ジョブ仕様」またはジョブ番号を表します。 これは、killfgbgなどの他のジョブおよびプロセス制御コマンドで、ジョブ番号の前にパーセント記号を付けることで参照できます。 この場合、このジョブを%1として参照します。

バックグラウンドプロセスの停止

いくつかの方法で現在のバックグラウンドプロセスを停止できます。 最も簡単な方法は、killコマンドを関連するジョブ番号とともに使用することです。 たとえば、次のように入力して、実行中のバックグラウンドプロセスを強制終了できます。

kill %1

端末の設定方法に応じて、すぐに、または次にEnterキーを押すと、ジョブの終了ステータスが表示されます。

Output[1]+  Terminated              command ping -i 5 google.com

jobsコマンドをもう一度確認すると、現在のジョブは表示されません。

プロセス状態の変更

バックグラウンドでプロセスを開始および停止する方法がわかったので、プロセスの状態を変更する方法について説明できます。

CTRL-Zを使用してプロセスを停止または一時停止する方法を説明したときに、1つの状態変化を示しました。 プロセスがこの停止状態にある場合、フォアグラウンドプロセスをバックグラウンドに移動したり、その逆を行うことができます。

前景プロセスを背景に移動する

コマンドを開始するときに&でコマンドを終了するのを忘れた場合でも、プロセスをバックグラウンドに移動できます。

最初のステップは、CTRL-Zでプロセスを再度停止することです。

CTRL-Z

プロセスが停止したら、bgコマンドを使用して、バックグラウンドでプロセスを再開できます。

bg

ジョブのステータス行が再び表示されますが、今回はアンパサンドが追加されています。

Output[1]+ ping -i 5 google.com &

デフォルトでは、bgコマンドは最後に停止したプロセスで動作します。 再起動せずに連続して複数のプロセスを停止した場合は、ジョブ番号でプロセスを参照して、正しいプロセスをバックグラウンドにできます。

すべてのコマンドをバックグラウンド化できるわけではないことに注意してください。 一部のプロセスは、アクティブな端末に直接接続された標準入出力で開始されたことを検出すると、自動的に終了します。

バックグラウンドプロセスをフォアグラウンドに移動する

fgと入力して、バックグラウンドプロセスをフォアグラウンドに移動することもできます。

fg

これは、最近バックグラウンドで実行されたプロセス( `+ jobs`出力の" "で示されます)で動作します。 プロセスを直ちに中断し、フォアグラウンドに配置します。 別のジョブを指定するには、そのジョブ番号を使用します。

fg %2

ジョブがフォアグラウンドになったら、CTRL-Cを使用してジョブを強制終了するか、完了させるか、一時停止して再度バックグラウンドにすることができます。

SIGHUPの取り扱い

プロセスがバックグラウンドであるかフォアグラウンドであるかに関係なく、プロセスを開始したターミナルインスタンスとかなり密接に結びついています。 端末が閉じると、通常、端末に関連付けられているすべてのプロセス(フォアグラウンド、バックグラウンド、または停止)にSIGHUPシグナルを送信します。 これは、制御端末がまもなく利用できなくなるため、プロセスが終了することを通知します。 ターミナルを閉じてもバックグラウンドプロセスを実行したい場合はどうしますか?

これを実現する方法はいくつかあります。 最も柔軟な方法は、通常、screentmuxなどのターミナルマルチプレクサを使用するか、少なくともdtachなどのそれらのデタッチ機能を提供するユーティリティを使用することです。

ただし、これは常にオプションとは限りません。 これらのプログラムが利用できない場合や、実行を継続するために必要なプロセスを既に開始している場合があります。 時々、これらはあなたが達成する必要があるものに対しては行き過ぎです。

nohupを使用する

プロセスの開始時に、プロセスが完了する前にターミナルを閉じたいことがわかっている場合は、nohupコマンドを使用してターミナルを開始できます。 これにより、開始されたプロセスはSIGHUPシグナルの影響を受けなくなります。 ターミナルが閉じても実行を継続します。 これは、initシステムの子として再割り当てされます。

nohup ping -i 5 google.com &

次のような行が表示されます。これは、コマンドの出力がnohup.outというファイルに書き込まれることを示します(書き込み可能な場合は現在のディレクトリにあり、そうでない場合はホームディレクトリにあります)。

Outputnohup: ignoring input and appending output to ‘nohup.out’

これは、ターミナルウィンドウが閉じられた場合に出力が失われないようにするためです。

ターミナルウィンドウを閉じて別のウィンドウを開いても、プロセスは実行されたままです。 各端末インスタンスは独自の独立したジョブキューを維持しているため、jobsコマンドの出力には表示されません。 ターミナルが閉じると、pingprocessがまだ実行されていても、pingjobが破棄されました。

pingプロセスを強制終了するには、そのプロセスID(または「PID」)を検索する必要があります。 これは、pgrepコマンドを使用して実行できます(pkillコマンドもありますが、この2つの部分からなる方法により、目的のプロセスのみを強制終了できます)。 pgrepおよび-aフラグを使用して、実行可能ファイルを検索します。

pgrep -a ping
Output7360 ping -i 5 google.com

その後、返されたPID(最初の列の数値)を参照することにより、プロセスを強制終了できます。

kill 7360

nohup.outファイルが不要になった場合は、削除することをお勧めします。

disownを使用する

nohupコマンドは役に立ちますが、プロセスの開始時に必要になることがわかっている場合に限ります。 bashジョブ制御システムは、disown組み込みコマンドを使用して同様の結果を達成する他の方法を提供します。

disownコマンドは、デフォルト構成で、端末のジョブキューからジョブを削除します。 これは、このガイドで説明されているジョブ制御メカニズム(fgbgCTRL-ZCTRL-Cなど)を使用して管理できなくなったことを意味します。 jobs出力のリストからすぐに削除され、端末に関連付けられなくなります。

コマンドは、ジョブ番号を指定して呼び出されます。 たとえば、ジョブ2をすぐに否認するには、次のように入力します。

disown %2

これにより、制御端末が閉じられた後、プロセスはnohupプロセスと同じ状態になります。 例外は、制御端末がファイルにリダイレクトされていない場合、制御端末を閉じると出力が失われることです。

通常、端末ウィンドウをすぐに閉じない場合、ジョブ制御からプロセスを完全に削除することは望ましくありません。 代わりに、-hフラグをdisownプロセスに渡して、プロセスにSIGHUPシグナルを無視するようにマークを付けることができますが、それ以外の場合は通常のジョブとして続行します。

disown -h %1

この状態では、通常のジョブ制御メカニズムを使用して、ターミナルを閉じるまでプロセスの制御を続けることができます。 ターミナルを閉じると、起動時にファイルにリダイレクトしなかった場合、再び出力する場所がないプロセスでスタックします。

これを回避するには、プロセスが既に実行された後に、プロセスの出力をリダイレクトしてみてください。 これはこのガイドの範囲外ですが、this postを見て、それをどのように行うかを理解することができます。

huponexitシェルオプションの使用

Bashには、子プロセスのSIGHUP問題を回避する別の方法もあります。 huponexitシェルオプションは、bashが終了時に子プロセスにSIGHUPシグナルを送信するかどうかを制御します。

Note

[。注意]##

huponexitオプションは、シェルセッションの終了がfrom within the shell itselfで開始された場合のSIGHUPの動作にのみ影響します。 これが当てはまる例としては、セッション内でexitコマンドまたはCTRL-Dがヒットした場合があります。

シェルセッションがターミナルプログラム自体を介して(ウィンドウを閉じるなどして)終了すると、コマンドhuponexitnoに影響します。 bashがSIGHUPシグナルを送信するかどうかを決定する代わりに、端末自体がSIGHUPシグナルをbashに送信し、bashはシグナルをその子プロセスに(正しく)伝播します。

上記の警告にもかかわらず、huponexitオプションはおそらく最も簡単なオプションの1つです。 次のように入力して、この機能がオンかオフかを確認できます。

shopt huponexit

オンにするには、次を入力します。

shopt -s huponexit

ここで、exitと入力してセッションを終了すると、プロセスはすべて実行され続けます。

exit

これには、最後のオプションと同じプログラム出力に関する警告があるため、端末を閉じる前に重要な場合はプロセスの出力をリダイレクトしたことを確認してください。

結論

ジョブ制御と、フォアグラウンドプロセスとバックグラウンドプロセスの管理方法を学習すると、コマンドラインでプログラムを実行する際の柔軟性が高まります。 多くのターミナルウィンドウまたはSSHセッションを開く必要はなく、多くの場合、いくつかの停止コマンドとバックグラウンドコマンドで対応できます。