Os processos em segundo plano recebem um SIGHUP ao fazer logoff?

21

Este é um acompanhamento para esta pergunta .

Eu fiz mais alguns testes; parece que realmente não importa se isso é feito no console físico ou via SSH, nem acontece apenas com o SCP; Eu também testei com cat /dev/zero > /dev/null. O comportamento é exatamente o mesmo:

  • Inicie um processo em segundo plano usando &(ou coloque-o em segundo plano depois de começar a usar CTRL-Ze bg); isso é feito sem usarnohup .
  • Sair.
  • Faça logon novamente.
  • O processo ainda está lá, rodando feliz, e agora é filho direto de init.

Posso confirmar que o SCP e o CAT são encerrados imediatamente se for enviado um SIGHUP; Eu testei isso usando kill -HUP.

Portanto, realmente parece que o SIGHUP não foi enviado no logoff, pelo menos para processos em segundo plano (não é possível testar com um primeiro plano por razões óbvias).

Isso aconteceu comigo inicialmente com o console de serviço do VMware ESX 3.5 (baseado no RedHat), mas consegui replicá-lo exatamente no CentOS 5.4.

A questão é: novamente, um SIGHUP não deve ser enviado aos processos, mesmo que estejam em execução em segundo plano, após o logoff? Por que isso não está acontecendo?


Editar

Eu verifiquei com strace, de acordo com a resposta de Kyle.
Como eu esperava, o processo não recebe nenhum sinal ao sair do shell onde foi iniciado. Isso acontece ao usar o console do servidor e via SSH.

Massimo
fonte
Usando o Bash no CentOS 7.1, um simples loop de script de shell obterá um SIGHUP se for deixado em primeiro plano, mas o terminal foi morto; strace de outro terminal mostra: --- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=10676, si_uid=3000090} --- rt_sigreturn() = -1 EINTR (Interrupted system call) rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
Mike S
O mesmo ocorrerá com um script em segundo plano. Observe que o terminal está fechado enquanto o loop está aguardando um sono. O shell NÃO sai:--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=13944, si_uid=3000090} --- +++ killed by SIGHUP +++
Mike S
Veja minha resposta para testes. Curiosamente, não vi nenhuma mudança de comportamento devido a huponexit.
Mike S

Respostas:

26

Resposta encontrada.

Para o BASH, isso depende da huponexitopção do shell, que pode ser visualizada e / ou definida usando o shoptcomando interno.

Parece que essas opções estão desativadas por padrão, pelo menos nos sistemas baseados em RedHat.

Mais informações na página do manual BASH :

O shell sai por padrão após o recebimento de um SIGHUP. Antes de sair, um shell interativo reenvia o SIGHUP para todos os trabalhos, em execução ou interrompidos. Os trabalhos interrompidos são enviados SIGCONT para garantir que eles recebam o SIGHUP. Para impedir que o shell envie o sinal para um trabalho específico, ele deve ser removido da tabela de trabalhos com o renegado incorporado (consulte SHELL BUILTIN COMMANDS abaixo) ou marcado para não receber SIGHUP usando disown -h.

Se a opção de shell huponexit tiver sido definida com shopt, o bash enviará um SIGHUP para todas as tarefas quando um shell de logon interativo sair.

Massimo
fonte
4
Verificado. Quando eu realizava uma "saída", "logout" ou CTL-D, o processo filho (trabalho) não receberia um suspiro (usuário root e reg). No entanto, quando eu "matei -HUP $$" para matar a instância atual do bash, os processos filho receberam um suspiro. Em seguida, configurei o huponexit e o processo filho recebeu o SIGHUP na saída.
CarpeNoctem
3

Será enviado SIGHUP nos meus testes:

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 Novamente:

[kbrandt@kbrandt-opadmin: ~] exit
zsh: you have running jobs.
[kbrandt@kbrandt-opadmin: ~] exit
zsh: warning: 1 jobs SIGHUPed
Connection to localhost closed.

Shell2 Novamente :

strace -e trace=signal -p1121
Process 1121 attached - interrupt to quit
pause()                                 = ? ERESTARTNOHAND (To be restarted)
--- SIGHUP (Hangup) @ 0 (0) ---
Process 1121 detached

Por que ele ainda roda ?:
A programação avançada no ambiente Unix de Stevens aborda isso na seção 9.10: Grupos de processos órfãos. A seção mais relevante é:

Como o grupo de processos fica órfão quando o pai termina, o POSIX.1 exige que todo processo no grupo de processos órfão que está parado (como nosso filho está) receba o sinal de desligamento (SIGHUP) seguido pelo sinal de continuação (SIGCONT )

Isso faz com que a criança continue, após o processamento do sinal de desligamento. A ação padrão para o sinal de desligamento é encerrar o processo, portanto, precisamos fornecer um manipulador de sinal para capturar o sinal. Portanto, esperamos que o printf na função sig_hup apareça antes do printf na função pr_ids.

Kyle Brandt
fonte
Mas você enviou explicitamente um SIGHUP para ele aqui; Eu estava falando sobre o que acontece quando você sai do shell em que iniciou o processo.
Massimo
Mesmos resultados quando digito exit, apesar de receber um aviso sobre trabalhos, mas digito exit novamente. Eu testei isso com ZSH.
Kyle Brandt
Estou usando o BASH, e isso provavelmente depende do shell. Mas o BASH deve enviar o SIGHUP para processos filho ao fazer logoff ...
Massimo
O Bash envia o SIGCONT aparentemente se o trabalho for interrompido, mas confirmo que não envia nada se o trabalho não foi interrompido.
Kyle Brandt
Usando o Bash no CentOS 7.1, um SIGTERM enviado para o meu processo é interrompido em outra janela: 1.) Inicie um shell script simples (loop com um eco e um sono), 2.) Control-Z, 3) trace o processo em outra janela, 4) saia do terminal original. Queixa-me de que tenho empregos em andamento e, depois de sair do meu rastro, mostro: $ strace -e signal -p1705 Process 1705 attached --- stopped by SIGTSTP --- --- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=791, si_uid=3000090} --- +++ killed by SIGTERM +++ Estranho, definitivamente não de acordo com a seção citada por Stevens.
Mike S
2

Fiz alguns testes usando o CentOS 7.1 e o bash. Observe que isso significa que huponexitestá offpor padrão e foi desativado na maioria dos meus testes.

Você precisa nohupquando inicia um trabalho em um terminal, porque se você fechar esse terminal sem sair do shell de forma limpa , o terminal envia o sinal SIGHUP ao shell, que então o envia a todas as crianças. Se você sair do shell de forma limpa, o que significa que o trabalho já deve estar em segundo plano, para que você possa digitar exitou pressionar Control-D no prompt de comando, nenhum sinal de qualquer tipo será enviado para o trabalho em segundo plano a partir do bash.

Teste:

Terminal 1

$ echo $$
16779

Terminal 2

$ strace -e signal -p16779
Process 16779 attached

(feche o terminal 1, visto no terminal 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 +++

Trabalho 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

Inicie em segundo plano no Terminal 1:

Terminal 1

$ ./doit.sh &
[1] 22954

Strace no Terminal 2; feche o Terminal 1 após alguns loops:

Terminal 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
...

Saída no terminal 3:

Terminal 3

out 1
out 2
out 3
HUP
out 4
out 5
out 6

No entanto, se você sair bash, simplesmente sai sem enviar nenhum sinal para a criança. O terminal sairá porque não tem mais um filho, mas é claro que não há ninguém no HUP porque o shell filho já se foi. O SIGINT, SIG_BLOCKe SIG_SETMASKvocê vê abaixo são devido ao sleepno shell.

Terminal 1

$ ./doit.sh &
26275

Terminal 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

Terminal 3, saída

out 1
out 2
out 3
out 4
out 5
out 6

Curiosamente, eu comecei huponexita trabalhar shopt -s huponexit; shopt(o último para revisar), depois realizei o último teste e, novamente, o bash não enviou nenhum sinal para o processo em segundo plano . Ainda mais interstingly, como vimos o bash fez enviar o sinal para o processo em segundo plano depois que ele recebeu de um terminal que fechou em seu rosto. Parece que huponexitnão tinha um rumo de um jeito ou de outro.

Espero que isso remova qualquer mistério ou confusão sobre pelo menos a felicidade do bash, sobre quando e como o sinal HUP é enviado. Pelo menos meus testes eram completamente reproduzíveis para mim. Gostaria de saber se existem outras configurações que possam estar afetando o comportamento do bash.

E, como sempre, YSMV (seu shell pode variar).

Adenda 1

Quando executo um shell como exec /bin/sh, em seguida, executo o script como /bin/sh ./doit.sh &e saio do shell corretamente, nenhum sinal é enviado para o trabalho em segundo plano e ele continua sendo executado até a conclusão.

Adenda 2

Quando executo um shell como exec /bin/csh, em seguida, executo o script como /bin/sh ./doit.sh &e saio do shell corretamente, nenhum sinal é enviado para o trabalho em segundo plano e ele continua sendo executado até a conclusão.

Mike S
fonte
0

Eu uso o csh e os processos em segundo plano continuam sendo executados quando eu faço o logoff.

Chris S
fonte