Řešení 1:
Odpověď nalezena.
U BASH to závisí na huponexit
shell, kterou lze zobrazit a/nebo nastavit pomocí vestavěného shopt
příkaz.
Zdá se, že tato možnost je ve výchozím nastavení vypnutá, alespoň na systémech založených na RedHat.
Více informací na manuálové stránce BASH:
Shell se standardně ukončí po přijetí SIGHUP. Před ukončením interaktivní shell znovu odešle SIGHUP všem úlohám, spuštěným i zastaveným. Zastavené úlohy jsou odeslány SIGCONT, aby bylo zajištěno, že obdrží SIGHUP. Aby shell neposílal signál konkrétní úloze, měl by být odstraněn z tabulky úloh s vestavěným disown (viz PŘÍKAZY SHELL BUILTIN níže) nebo označen, aby nepřijímal SIGHUP pomocí disown -h.
Pokud byla možnost huponexit shell nastavena pomocí shopt, bash odešle SIGHUP všem úlohám, když se interaktivní přihlašovací shell ukončí.
Řešení 2:
Bude odesláno SIGHUP v mých testech:
Shell1:
[[email protected]: ~] ssh localhost
[[email protected]: ~] perl -e sleep &
[1] 1121
[[email protected]: ~] 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:
[[email protected]: ~] exit
zsh: you have running jobs.
[[email protected]: ~] exit
zsh: warning: 1 jobs SIGHUPed
Connection to localhost closed.
Znovu Shell2 :
strace -e trace=signal -p1121
Process 1121 attached - interrupt to quit
pause() = ? ERESTARTNOHAND (To be restarted)
--- SIGHUP (Hangup) @ 0 (0) ---
Process 1121 detached
Proč stále běží?:
Pokročilé programování v prostředí Unix od Stevense to pokrývá v části 9.10:Orphaned Process Groups. Nejrelevantnější sekce je:
Vzhledem k tomu, že skupina procesů je osiřelá, když rodič skončí, POSIX.1 vyžaduje, aby každý proces v nově osiřelé skupině procesů, který je zastaven (stejně jako naše dítě), byl odeslán signál zavěšení (SIGHUP) následovaný signálem pokračování (SIGCONT).
To způsobí, že dítě bude pokračovat po zpracování signálu zavěšení. Výchozí akcí pro signál zavěšení je ukončit proces, takže musíme poskytnout obsluhu signálu, která signál zachytí. Proto očekáváme, že printf ve funkci sig_hup se objeví před printf ve funkci pr_ids.
Řešení 3:
Provedl jsem nějaké testy pomocí CentOS 7.1 a bash. Všimněte si, že to znamená huponexit
je off
ve výchozím nastavení a po většinu mých testů byl vypnutý.
Potřebujete nohup
když zahájíte úlohu v terminálu, protože pokud tento terminál zavřete, aniž byste opustili prostředí shell , terminál odešle bash signál SIGHUP do shellu, který jej pak odešle všem dětem. Pokud opustíte shell čistě - to znamená, že úloha již musí být na pozadí, takže můžete zadat exit
nebo stiskněte Control-D na příkazovém řádku – do úlohy na pozadí se z bash neposílají žádné signály.
Test:
Terminál 1
$ echo $$
16779
Terminál 2
$ strace -e signal -p16779
Process 16779 attached
(zavřete svorku 1, viz svorku 2):
--- 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 +++
Úloha 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
Spusťte jej na pozadí v Terminálu 1:
Terminál 1
$ ./doit.sh &
[1] 22954
Vyhledejte to v Terminálu 2; zavřete terminál 1 po několika smyčkách:
Terminál 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
...
Výstup na terminálu 3:
Terminál 3
out 1
out 2
out 3
HUP
out 4
out 5
out 6
Pokud však ukončíte bash
, jednoduše odejde, aniž by dítěti vyslalo jakýkoli signál. Terminál se ukončí, protože již nemá potomka, ale samozřejmě není nikdo, kdo by HUP, protože podřízený shell je již pryč. SIGINT
, SIG_BLOCK
a SIG_SETMASK
jak vidíte níže, jsou způsobeny sleep
ve skořápce.
Terminál 1
$ ./doit.sh &
26275
Terminál 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
Terminál 3, výstup
out 1
out 2
out 3
out 4
out 5
out 6
Zajímavé je, že jsem nastavil huponexit
být zapnutý s shopt -s huponexit; shopt
(posledně jmenovaný nákup ke kontrole), poté provedl poslední test a znovu bash neodeslal procesu na pozadí žádný signál . Ještě zajímavější, jak jsme viděli bash udělal odeslat signál do procesu na pozadí poté, co jej přijal z terminálu, který se mu zavřel. Zdá se, že huponexit
nesnášel ani jednu, ani druhou stranu.
Doufám, že to odstraní jakoukoli záhadu nebo zmatek týkající se alespoň bashova hupiness, kdy a jak je vysílán signál HUP. Alespoň pro mě byly mé testy zcela reprodukovatelné. Zajímalo by mě, jestli existují nějaká další nastavení, která mohou ovlivňovat chování bash.
A jako vždy YSMV (Your Shell May Vary).
Dodatek 1
Když spustím shell jako exec /bin/sh
a poté spusťte skript jako /bin/sh ./doit.sh &
, poté čistě opusťte shell, do úlohy na pozadí se neposílají žádné signály a úloha pokračuje až do dokončení.
Dodatek 2
Když spustím shell jako exec /bin/csh
a poté spusťte skript jako /bin/sh ./doit.sh &
, poté čistě opusťte shell, do úlohy na pozadí se neposílají žádné signály a úloha pokračuje až do dokončení.