前書き
このガイドでは、bash
、Linuxシステム、および端末がどのように連携してプロセスとジョブ制御を提供するかについて説明します。 previous guideでは、ps
、kill
、および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]
は、コマンドの「ジョブ仕様」またはジョブ番号を表します。 これは、kill
、fg
、bg
などの他のジョブおよびプロセス制御コマンドで、ジョブ番号の前にパーセント記号を付けることで参照できます。 この場合、このジョブを%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シグナルを送信します。 これは、制御端末がまもなく利用できなくなるため、プロセスが終了することを通知します。 ターミナルを閉じてもバックグラウンドプロセスを実行したい場合はどうしますか?
これを実現する方法はいくつかあります。 最も柔軟な方法は、通常、screen
やtmux
などのターミナルマルチプレクサを使用するか、少なくともdtach
などのそれらのデタッチ機能を提供するユーティリティを使用することです。
ただし、これは常にオプションとは限りません。 これらのプログラムが利用できない場合や、実行を継続するために必要なプロセスを既に開始している場合があります。 時々、これらはあなたが達成する必要があるものに対しては行き過ぎです。
nohupを使用する
プロセスの開始時に、プロセスが完了する前にターミナルを閉じたいことがわかっている場合は、nohup
コマンドを使用してターミナルを開始できます。 これにより、開始されたプロセスはSIGHUPシグナルの影響を受けなくなります。 ターミナルが閉じても実行を継続します。 これは、initシステムの子として再割り当てされます。
nohup ping -i 5 google.com &
次のような行が表示されます。これは、コマンドの出力がnohup.out
というファイルに書き込まれることを示します(書き込み可能な場合は現在のディレクトリにあり、そうでない場合はホームディレクトリにあります)。
Outputnohup: ignoring input and appending output to ‘nohup.out’
これは、ターミナルウィンドウが閉じられた場合に出力が失われないようにするためです。
ターミナルウィンドウを閉じて別のウィンドウを開いても、プロセスは実行されたままです。 各端末インスタンスは独自の独立したジョブキューを維持しているため、jobs
コマンドの出力には表示されません。 ターミナルが閉じると、ping
processがまだ実行されていても、ping
jobが破棄されました。
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
コマンドは、デフォルト構成で、端末のジョブキューからジョブを削除します。 これは、このガイドで説明されているジョブ制御メカニズム(fg
、bg
、CTRL-Z
、CTRL-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
がヒットした場合があります。
シェルセッションがターミナルプログラム自体を介して(ウィンドウを閉じるなどして)終了すると、コマンドhuponexit
はnoに影響します。 bash
がSIGHUPシグナルを送信するかどうかを決定する代わりに、端末自体がSIGHUPシグナルをbash
に送信し、bash
はシグナルをその子プロセスに(正しく)伝播します。
上記の警告にもかかわらず、huponexit
オプションはおそらく最も簡単なオプションの1つです。 次のように入力して、この機能がオンかオフかを確認できます。
shopt huponexit
オンにするには、次を入力します。
shopt -s huponexit
ここで、exit
と入力してセッションを終了すると、プロセスはすべて実行され続けます。
exit
これには、最後のオプションと同じプログラム出力に関する警告があるため、端末を閉じる前に重要な場合はプロセスの出力をリダイレクトしたことを確認してください。
結論
ジョブ制御と、フォアグラウンドプロセスとバックグラウンドプロセスの管理方法を学習すると、コマンドラインでプログラムを実行する際の柔軟性が高まります。 多くのターミナルウィンドウまたはSSHセッションを開く必要はなく、多くの場合、いくつかの停止コマンドとバックグラウンドコマンドで対応できます。