この質問 のフォローアップです。
さらにいくつかのテストを実行しました。これが物理的なコンソールで行われるか、SSH経由で行われるかは問題ではないようです。また、これはSCPでのみ発生します。 cat /dev/zero > /dev/null
でもテストしました。動作はまったく同じです。
&
を使用してバックグラウンドでプロセスを開始します(またはCTRL-Z
とbg
を使用して開始した後にバックグラウンドで実行します);これはNohup
を使用せずに行われます。init
の直接の子になっています。SIGHUP
を送信すると、SCPとCATの両方がすぐに終了することを確認できます。私はkill -HUP
を使用してこれをテストしました。
そのため、SIGHUPはログオフ時に not sent のように見えます。少なくともバックグラウンドプロセスに対して(明らかな理由でフォアグラウンドプロセスでテストすることはできません)。
これは当初、VMware ESX 3.5(RedHatベース)のサービスコンソールで起こりましたが、CentOS 5.4で正確に複製することができました。
ここでも問題です。ログオフ時に、バックグラウンドで実行されている場合でも、SIGHUPをプロセスに送信しないでください。なぜこれが起こらないのですか?
カイルの答えに従って、私はstrace
で確認しました。
予想していたように、起動されたシェルからログオフしても、プロセスは any シグナルを取得しません。これは、サーバーのコンソールを使用する場合とSSH経由の場合の両方で発生します。
回答が見つかりました。
BASHの場合、これはhuponexit
シェルオプションに依存します。これは、組み込みshopt
コマンドを使用して表示または設定できます。
このオプションは、少なくともRedHatベースのシステムでは、デフォルトでオフになっているようです。
シェルは、SIGHUPを受信するとデフォルトで終了します。終了する前に、対話型シェルは実行中または停止中のすべてのジョブにSIGHUPを再送信します。停止したジョブはSIGCONTに送信され、SIGHUPを確実に受信します。シェルが特定のジョブにシグナルを送信しないようにするには、disownビルトイン(下記のシェルの組み込みコマンドを参照)を使用してジョブテーブルから削除するか、disown -hを使用してSIGHUPを受信しないようにマークする必要があります。
Shoptでhuponexitシェルオプションが設定されている場合、bashは、対話型ログインシェルの終了時にすべてのジョブにSIGHUPを送信します。
私のテストではSIGHUPが送信されます:
Shell1:
[kbrandt@kbrandt-opadmin: ~] ssh localhost
[kbrandt@kbrandt-opadmin: ~] Perl -e sleep &
[1] 1121
[kbrandt@kbrandt-opadmin: ~] ps
PID TTY TIME CMD
1034 pts/46 00:00:00 zsh
1121 pts/46 00:00:00 Perl
1123 pts/46 00:00:00 ps
Shell2:
strace -e trace=signal -p1121
Shell1 Again:
[kbrandt@kbrandt-opadmin: ~] exit
zsh: you have running jobs.
[kbrandt@kbrandt-opadmin: ~] exit
zsh: warning: 1 jobs SIGHUPed
Connection to localhost closed.
Shell2 Again:
strace -e trace=signal -p1121
Process 1121 attached - interrupt to quit
pause() = ? ERESTARTNOHAND (To be restarted)
--- SIGHUP (Hangup) @ 0 (0) ---
Process 1121 detached
なぜまだ動作するのですか?:
StevensによるUnix環境での高度なプログラミングは、セクション9.10:孤立したプロセスグループでこれをカバーしています。最も関連するセクションは次のとおりです。
親が終了するとプロセスグループは孤立するため、POSIX.1では、(子がそうであるように)停止した新しく孤立したプロセスグループ内のすべてのプロセスに、ハングアップ信号(SIGHUP)に続いて続行信号(SIGCONT)を送信する必要があります。 )。
これにより、ハングアップ信号を処理した後、子が続行されます。ハングアップシグナルのデフォルトのアクションはプロセスを終了することなので、シグナルをキャッチするシグナルハンドラーを提供する必要があります。したがって、sig_hup関数のprintfは、pr_ids関数のprintfの前に表示されると想定しています。
CentOS 7.1とbashを使用していくつかのテストを実行しました。これは、huponexit
がデフォルトでoff
であり、私のテストの大部分ではオフであったことを意味します。
シェルをきれいに終了せずにそのターミナルを閉じると、ターミナルので、ターミナルでジョブを開始するときにNohup
が必要ですSIGHUPシグナルをbashをシェルに送信し、シェルはそれをすべての子に送信します。シェルをきれいに終了する場合-ジョブは既にバックグラウンドにある必要があるため、exit
を入力するか、コマンドプロンプトでControl-Dを押すことができます。bashからバックグラウンドジョブに信号が送信されません。
テスト:
ターミナル1
$ echo $$
16779
ターミナル2
$ strace -e signal -p16779
Process 16779 attached
(ターミナル2に表示されるターミナル1を閉じる):
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=16777, si_uid=3000090} ---
rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], 8) = 0
rt_sigprocmask(SIG_SETMASK, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], NULL, 8) = 0
rt_sigaction(SIGHUP, {SIG_DFL, [], SA_RESTORER, 0x7f7ace3d9a00}, {0x456880, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x7f7ace3d9a00}, 8) = 0
kill(16779, SIGHUP) = 0
rt_sigreturn() = -1 EINTR (Interrupted system call)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=16779, si_uid=3000090} ---
+++ killed by SIGHUP +++
ジョブdoit.sh
:
#!/bin/bash
imhupped() {
echo "HUP" >> /tmp/outfile
}
trap imhupped SIGHUP
for i in $(seq 1 6); do echo out $i >> /tmp/outfile; sleep 5; done
ターミナル1のバックグラウンドで起動します。
ターミナル1
$ ./doit.sh &
[1] 22954
ターミナル2でトレースします。数回のループの後、ターミナル1を閉じます。
ターミナル2
$ strace -e signal -p22954
Process 22954 attached
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=22980, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f7a5d547a00}, {0x43e4b0, [], SA_RESTORER, 0x7f7a5d547a00}, 8) = 0
...
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=21685, si_uid=3000090} ---
rt_sigreturn() = -1 EINTR (Interrupted system call)
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=23017, si_status=SIGHUP, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
...
ターミナル3の出力:
ターミナル
out 1
out 2
out 3
HUP
out 4
out 5
out 6
ただし、bash
を終了すると、子にシグナルを送信せずに終了します。子がなくなったため端末は終了しますが、子シェルがすでになくなっているため、HUPを実行するユーザーがいません。以下に表示されるSIGINT
、SIG_BLOCK
、SIG_SETMASK
は、シェルのsleep
が原因です。
ターミナル1
$ ./doit.sh &
26275
ターミナル2
$ strace -e signal -p26275
Process 26275 attached
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26280, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGINT, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
(..."exit" is typed in bash, notice no new signals sent...)
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=26303, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGINT, {0x43e4b0, [], SA_RESTORER, 0x7f5edd3a5a00}, {SIG_DFL, [], SA_RESTORER, 0x7f5edd3a5a00}, 8) = 0
端子3、出力
out 1
out 2
out 3
out 4
out 5
out 6
興味深いことに、私はhuponexit
をshopt -s huponexit; shopt
でオンに設定し(後者のショップは確認する)、最後のテストを実行しましたbashはシグナルをバックグラウンドプロセスに送信しませんでした 。さらに興味深いことに、bashdidは、顔を閉じた端末からシグナルを受信した後で、シグナルをバックグラウンドプロセスに送信します。 huponexit
にはどちらの方向にも関係がないようです。
これにより、HUP信号がいつどのように送信されるかについて、少なくともbashの幸福に関する謎や混乱が解消されることを願っています。少なくとも、私のテストは完全に再現可能でした。 bashの動作に影響を与える可能性のある他の設定があるかどうかを知りたいです。
そして、いつものように、YSMV(あなたのシェルは変わるかもしれません)。
補遺1
シェルをexec /bin/sh
として実行してから、スクリプトを/bin/sh ./doit.sh &
として実行し、シェルをきれいに終了すると、バックグラウンドジョブに信号が送信されず、実行が完了します。
補遺2
シェルをexec /bin/csh
として実行し、スクリプトを/bin/sh ./doit.sh &
として実行し、シェルを正常に終了すると、バックグラウンドジョブに信号が送信されず、実行が完了します。
私はキャッシュを使用しており、ログオフしてもバックグラウンドプロセスが実行され続けます。