web-dev-qa-db-ja.com

起動したターミナルを閉じると、Linuxはバックグラウンドプロセスを強制終了しますか?

組み込みシステムがあります。その上でtelnetを実行してから、アプリケーションをバックグラウンドで実行します。

./app_name &

端末を閉じて他の端末からtelnetを実行し、確認すると、このプロセスがまだ実行中であることがわかります。

これを確認するために、小さなプログラムを作成しました。

#include<stdio.h>
main()
{
    while(1);
}

このプログラムをローカルのLinux PCでバックグラウンドで実行し、ターミナルを閉じました。

今、他の端末からこのプロセスをチェックしたところ、このプロセスも強制終了されていることがわかりました。

私の質問は:

  • 同じタイプのプロセスで未定義の動作が発生するのはなぜですか?
  • どちらに依存していますか?
  • Linuxのバージョンに依存していますか?
21
Chirag

誰が仕事を殺すべきか?

通常、フォアグラウンドジョブとバックグラウンドジョブは、さまざまな状況でカーネルまたはシェルから送信されたSIGHUPによって強制終了されます。


カーネルはいつSIGHUPを送信しますか?

KernelSIGHUPcontrolling process に送信します:

  • 実際の(ハードウェア)端末の場合:端末ドライバーで切断が検出されたとき。モデム回線の切断時。
  • for pseudoterminal (pty):ptyのマスター側を参照する最後の記述子が閉じられたとき。端末ウィンドウを閉じたとき。

KernelSIGHUPを他のプロセスグループに送信します。

  • フォアグラウンドプロセスグループへ、制御プロセスが終了したとき;
  • 孤立したプロセスグループ に孤立し、メンバーが停止した場合。

制御プロセスは、制御端末への接続を確立したセッションリーダーです。

通常、制御プロセスはシェルです。要約すると、

  • カーネルは、実ターミナルまたは疑似ターミナルが切断/クローズされると、SIGHUPをシェルに送信します。
  • シェルが終了すると、カーネルはSIGHUPをフォアグラウンドプロセスグループに送信します。
  • 停止したプロセスが含まれている場合、カーネルは孤立したプロセスグループにSIGHUPを送信します。

カーネルは、停止したプロセスが含まれていない場合、バックグラウンドプロセスグループにSIGHUPを送信しないことに注意してください。


bashはいつSIGHUPを送信しますか?

BashSIGHUPallジョブ(フォアグラウンドおよびバックグラウンド)に送信します:

  • SIGHUPを受信し、対話型のシェルである(およびジョブ制御サポートがコンパイル時に有効になっている場合);
  • それが終了すると、それは対話型ログインシェルであり、huponexitオプションが設定されます(およびジョブ制御サポートがコンパイル時に有効になります)。

詳細はこちら こちら

ノート:

  • bashは送信しないSIGHUPdisownを使用してジョブリストから削除したジョブに送信します。
  • NohupignoreSIGHUPを使用して開始されたプロセス。

詳細 こちら


他のシェルはどうですか?

通常、シェルはSIGHUPを伝播します。通常の終了時にSIGHUPを生成することはあまり一般的ではありません。


TelnetまたはSSH

TelnetまたはSSHの場合、接続が閉じられると(たとえば、PCでtelnetウィンドウを閉じると)次のようになります。

  1. クライアントが殺されました。
  2. サーバーはクライアント接続が閉じられたことを検出します。
  3. サーバーはptyのマスター側を閉じます。
  4. カーネルはマスターptyが閉じていることを検出し、SIGHUPbashに送信します。
  5. bashSIGHUPを受信し、SIGHUPをすべてのジョブに送信して終了します。
  6. 各ジョブはSIGHUPを受け取り、終了します。

問題

bashまたはtelnetd SSHサーバーからbusyboxおよびdropbearを使用して問題を再現できます:sometimes、クライアント接続が閉じられると、バックグラウンドジョブはSIGHUPを受け取りません(そして終了しません)。

サーバー(telnetdまたはdropbear)がptyのマスター側を閉じると、競合状態が発生するようです:

  1. 通常、bashSIGHUPを受け取り、バックグラウンドジョブを(予期したとおりに)即座に強制終了して終了します。
  2. ただし、bashは、ptybeforeEOFを処理する前に、スレーブ側でSIGHUPを検出することがあります。

bashEOFを検出すると、デフォルトではSIGHUPを送信せずにすぐに終了します。そして、バックグラウンドジョブは実行中のままです!


解決

通常の出口(bashを含む)でSIGHUPを送信するようにEOFを構成することもできます。

  • bashlogin Shellとして開始されていることを確認します。 huponexitworks ログインシェルの場合のみ、AFAIK。

    ログインシェルは、-lオプションまたはargv[0]の-​​ 先頭のハイフン で有効にします。ログインシェルモードで/bin/bash -lを呼び出す/bin/login以上の/bin/shを実行するようにtelnetdを設定できます。

    例えば。:

     telnetd -l /bin/login
    
  • huponexitオプションを有効にします。

    例えば。:

     shopt -s huponexit 
    

    毎回これをbashセッションに入力するか、.bashrcまたは/etc/profileに追加します。


なぜレースが発生するのですか?

bashは、安全な場合にのみ信号のブロックを解除し、一部のコードセクションを信号ハンドラーによって安全に中断できない場合に信号をブロックします。

そのようなクリティカルセクション中断ポイントを時々呼び出し、クリティカルセクションが実行されるときにシグナルが受信されると、そのハンドラーは次の中断ポイントが発生するか、クリティカルセクションが終了するまで遅延します。

ソースコードの quit.h から掘り始めることができます。

したがって、私たちの場合、bashはクリティカルセクションにあるときにSIGHUPを受け取ることがあります。 SIGHUPハンドラーの実行が遅延し、bashEOFを読み取り、クリティカルセクションを終了するか、次の割り込みポイントを呼び出すbeforeの前に終了します。


参照

  • "Job Control" 公式Glibcマニュアルのセクション。
  • 「The Linux Programming Interface」の第34章「プロセスグループ、セッション、およびジョブ制御」。
34
gavv

ターミナルを閉じると、ShellはSIGHUPをすべてのバックグラウンドプロセスに送信し、それによってプロセスが強制終了されます。これは、いくつかの方法で抑制できます。最も顕著な例は次のとおりです。

ノーフ

Nohupを指定してプログラムを実行すると、SIGHUPをキャッチしてプログラム出力をリダイレクトします。

$ Nohup app &

捨てる

disownは、シェルにSIGHUPを送信しないように指示します

$ app &
$ disown

Linuxのバージョンに依存していますか?

シェルによって異なります。上記は少なくともbashに適用されます。

5
pacholik

何が起こっているのかを完全に理解するには、unix内部に少し入る必要があります。

このようなコマンドを実行しているとき

./app_name &

app_nameがバックグラウンドプロセスグループに送信されます。 unixプロセスグループについて確認できます ここ

通常の終了でbashを閉じると、すべてのジョブに対してSIGHUPハングアップシグナルがトリガーされます。 unixジョブ制御に関するいくつかの情報は here です。

bashの終了時にアプリを実行し続けるには、Nohupユーティリティを使用して、アプリがハングアップシグナルの影響を受けないようにする必要があります。

Nohup-ハングアップの影響を受けないコマンドを実行し、非ttyに出力します

そして最後に、これはあなたがそれを行う必要がある方法です。

Nohup app_name & 2> /dev/null;

4
deimus

どちらの場合もAFAIKはプロセスを強制終了する必要があります。これを回避するには、次のようなNohupを発行する必要があります。

> Nohup ./my_app &

このようにして、プロセスは実行を継続します。おそらくtelnetの部分は、次のようなバグが原因です。

https://bugzilla.redhat.com/show_bug.cgi?id=8965

3
rkachach