16

次のようなbashスクリプトstart.shがあります。

for thing in foo bar; do
    {
        background_processor $thing
        cleanup_on_exit $thing
    } &
done

これは私が望むことです: start.sh を実行すると、コード 0 で終了し、2 つのサブシェルがバックグラウンドで実行されます。各サブシェルが実行background_processorされ、それが終了すると実行されcleanup_on_exitます。これは、最初に start.sh を実行した端末を終了しても機能します (それが ssh 接続であっても)。

それから私はこれを試しました:

ssh user@host "start.sh"

これは機能しstart.shますが、終了した後、ssh は明らかにサブシェルの終了も待機します。理由がよくわかりません。終了するとstart.sh、サブシェルは pid 1 の子になり、tty が割り当てられていないため、ssh 接続にどのように関連付けられているのか理解できません。

私は後でこれを試しました:

ssh -t user@host "start.sh"

これで、プロセスに疑似 tty が割り当てられました。現在、ssh が終了するとすぐに終了することがわかりましたstart.shが、子プロセスも強制終了されます。

後者の場合、子プロセスに SIGHUP が送信されていると推測したので、次のようにしました。

ssh -t user@host "nohup start.sh"

それは実際に機能します!したがって、私は実際の問題に対する解決策を持っていますが、ここで SIGHUP/tty の微妙な点を把握したいと思います。

要約すると、私の質問は次のとおりです。

  1. start.sh親の pid が 1 であるにもかかわらず、ssh (-t なし) が終了後も子プロセスを待機するのはなぜですか?
  2. 端末から子プロセスを実行してその端末からログアウトすると発生しないのに、ssh (-t を使用) が明らかに SIGHUP で子プロセスを強制終了するのはなぜですか?
4

3 に答える 3

23

私は今これを説明できると思います!TTY Demystifiedを読んで、セッションとプロセスグループについて少し学ぶ必要がありました。

  1. 親pid1があるのに、start.shが終了した後でもssh(-tなし)が子プロセスを待機するのはなぜですか?

ttyがないため、sshはパイプを介してシェルプロセスのstdin / stdout / stderrに接続し(その後、子に継承されます)、使用しているバージョンのOpenSSH(OpenSSH_4.3p2)は、これらのソケットが閉じるのを待ちます。終了します。OpenSSHの以前のバージョンの中には、そのように動作しなかったものがあります。これについては、理論的根拠とともに、ここに適切な説明があります。

逆に、対話型ログイン(またはssh -t)を使用する場合、sshとプロセスはTTYを使用しているため、待機するパイプはありません。

ストリームをリダイレクトすることで、必要な動作を回復できます。このバリアントはすぐに戻ります:ssh user@host "start.sh < /dev/null > /dev/null 2>&1"

  1. ターミナルから実行してそのターミナルからログアウトしても、ssh(-tを使用)が子プロセスを強制終了するのはなぜですか(明らかにSIGHUPを使用)。

bashは非対話型モードで開始しているため、これはジョブ制御がデフォルトで無効になっていることを意味し、その結果、子プロセスは親bashプロセス(セッションリーダー)と同じプロセスグループにあります。親bashプロセスが終了すると、カーネルは次のようにSIGHUPをそのプロセスグループ(フォアグラウンドにある)に送信しますsetpgid(2)

セッションに制御端末があり、... [および]セッションリーダーが終了すると、SIGHUP信号が制御端末のフォアグラウンドプロセスグループ内の各プロセスに送信されます。

逆に、インタラクティブログインを使用する場合、bashはインタラクティブモードになります。つまり、ジョブ制御はデフォルトで有効になっているため、子プロセスは別のプロセスグループに入り、終了時にSIGHUPを受信しません。

set -mbashでジョブ制御を有効にするために使用することで、必要な動作を回復できます。に追加set -mするとstart.sh、sshが終了したときに子が殺されることはなくなりました。

謎が解けた:)

于 2013-02-14T02:16:11.397 に答える
0

tty がない場合、bash は SIGHUP をフォークされたプロセスに渡し、シグナル自体を処理し、それを静かに無視して SSH セッションを結び付け続けているのではないかと思います (ただし、私は仮定しています)。

ただし、ユーザーとプロセスの間に tty があると、tty ドライバーは SIGHUP をインターセプトし、ユーザーを失ったことを認識し、親としての ssh セッションなしで実行するようにフォークします。

于 2013-02-05T01:52:30.770 に答える
0

この SIGHUP を発生させたくない呼び出しの先頭に「nohup」を追加します。

于 2014-06-23T15:17:09.637 に答える