
Por favor, explique isso:
#!/bin/bash
# This is scripta.sh
./scriptb.sh &
pid=$!
echo $pid started
sleep 3
while true
do
kill -SIGINT $pid
echo scripta.sh $$
sleep 3
done
-
#!/bin/bash
# This is scriptb.sh
trap "echo Ouch;" SIGINT
while true
do
echo scriptb.sh $$
sleep 1
done
Quando executo ./scripta.sh
o trap não imprime. Se eu mudar de SIGINT para qualquer outro sinal (tentei SIGTERM, SIGUSR1), a armadilha imprimirá "Ai" conforme o esperado. Como isso pode acontecer?
Responder1
Se eu mudar de SIGINT para qualquer outro sinal (tentei SIGTERM, SIGUSR1), o trap imprimirá “Ai” conforme o esperado.
Aparentemente você não experimentou o SIGQUIT; você provavelmente descobrirá que ele se comporta da mesma forma que o SIGINT.
O problema é o controle do trabalho.
Nos primórdios do Unix, sempre que o shell colocava um processo ou pipeline em segundo plano, ele configurava esses processos para ignorar SIGINT e SIGQUIT, de modo que não seriam encerrados se o usuário digitasse posteriormente Ctrl+ C(interromper) ou Ctrl+ \(sair) para uma tarefa em primeiro plano. Quando o controle de tarefas surgiu, ele trouxe consigo grupos de processos e agora tudo o que o shell precisa fazer é colocar a tarefa em segundo plano em um novo grupo de processos; contanto que esse não seja o grupo de processos do terminal atual, os processos não verão sinais vindos do teclado ( Ctrl+ C, Ctrl+ \ e Ctrl+ Z(SIGTSTP)). O shell pode deixar processos em segundo plano com a disposição de sinal padrão. Na verdade, provavelmente será necessário, para que os processos possam ser eliminados por Ctrl+ Cquando forem trazidos para o primeiro plano.
Mas os shells não interativos não usam controle de trabalho. Faz sentido que um shell não interativo retorne ao antigo comportamento de ignorar SIGINT e SIGQUIT para processos em segundo plano, pela razão histórica - para permitir que os processos em segundo plano continuem a ser executados, mesmo que sinais do tipo teclado sejam enviados a eles . E os scripts de shell são executados em shells não interativos.
E, se você olhar o último parágrafo sob o trap
comando emfestança(1), Você vai ver
Os sinais ignorados na entrada no shell não podem ser capturados ou redefinidos.
Então, se você fugir ./scriptb.sh &
do seuinterativoprompt de comando do shell, suas disposições de sinal são deixadas de lado (mesmo que estejam sendo colocadas em segundo plano) e o trap
comando funciona conforme o esperado. Mas, se você executar ./scripta.sh
(com ou sem &
), o script será executado em um shell não interativo. E quando esse shell não interativo é executado ./scriptb.sh &
, ele define o scriptb
processo para ignorar a interrupção e encerrar. E, portanto, o trap
comando scriptb.sh
falha silenciosamente.
Responder2
Com alguns rastreamentos:
strace -o aaa ./scripta
podemos observar que por padrão
read(255, "#!/bin/bash\n# this is scripta.sh"..., 153) = 153
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, -108, SEEK_CUR) = 45
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1ee9b7ca10) = 2483
assim scripta
bloqueou INT
e CHLD
sinaliza; scriptb
herda essas configurações por meio do fork
(aqui chamado clone
). E o que está scriptb
fazendo? Se executarmos scripta
via:
strace -o bbb ./scriptb &
E então procuramos coisas relacionadas a sinais, encontramos:
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_IGN, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, 8) = 0
O que indica que nada está sendo bloqueado e, em seguida, que os INT
sinais recebem primeiro o tratamento padrão e depois são ignorados. scriptb
executado diretamente do shell strace
em contraste mostra:
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
rt_sigaction(SIGINT, {0x45fbf0, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [INT CHLD], 8) = 0
...
Ou nunca ignorado, antes de entrar no então repetido tratamento de chamadas de sono. OK. Uh. Vamos colocar um calço entre scripta
e scriptb
isso redefine SIGINT
o estado padrão...
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "h?")) != -1) {
switch (ch) {
case 'h':
case '?':
default:
abort();
}
}
argc -= optind;
argv += optind;
if (argc < 1) abort();
signal(SIGINT, SIG_DFL);
execvp(*argv, argv);
abort();
return 1;
}
Usado da seguinte forma:
$ make defaultsig
cc defaultsig.c -o defaultsig
$ grep defaultsig scripta.sh
./defaultsig ./scriptb.sh &
$ ./scripta.sh
12510 started
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
...
Sim, funciona bem agora. Porém, não sei por que bash
se comporta dessa maneira, talvez registrar um bug com eles? O código sig.c
parece muito complicado e há mais manipulação de sinais em outros lugares...
Responder3
Problema estranho com trap e SIGINT
Obrigado a todos pelas respostas e pelo tempo que dedicaram ao estudo do problema.
Por favor, permita-me recapitular e integrar (com desculpas pelo que pode ser óbvio a seguir):
1) Esqueci de acrescentar na minha pergunta que também tentei o SIGQUIT e ele se comportou como SIGINT;
2) A partir disso, já suspeitava que o problema estava relacionado à disposição padrão de um bash interativo para esses dois sinais;
3) A ação padrão que não acontece ao interagir com o bash é porque não faz sentido sair ou interromper nada quando a única coisa que você tem é o prompt. Se desejar sair do shell, basta digitar exit;
4) Não vejo o SIGQUIT e o SIGINT desempenhando um papel especial no controle do trabalho (ao contrário do SIGTSTP, SIGTTOU, SIGTTIN);
5) Não faz sentido para mim que a disposição padrão de um bash interativo para esses dois sinais seja herdada por um shell de segundo plano (não interativo) (aquele que executa scriptb.sh no nosso caso);
6) Na verdade, assim como o grupo de processos em primeiro plano não herda (do shell que o iniciou) as disposições para SIGQUIT e SIGINT, IMHO deveria fazer sentido que o mesmo acontecesse com os grupos de processos em segundo plano.
7) Além disso, qualquer que seja a disposição herdada, a armadilha deve mudá-la.
8) Em suma, estou inclinado a concordar com o thrig e a pensar que o que estamos vendo aqui é um bug.