Problema estranho com trap e SIGINT

Problema estranho com trap e SIGINT

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.sho 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 trapcomando 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 trapcomando 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 scriptbprocesso para ignorar a interrupção e encerrar. E, portanto, o trapcomando scriptb.shfalha 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 scriptabloqueou INTe CHLDsinaliza; scriptbherda essas configurações por meio do fork(aqui chamado clone). E o que está scriptbfazendo? Se executarmos scriptavia:

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 INTsinais recebem primeiro o tratamento padrão e depois são ignorados. scriptbexecutado diretamente do shell straceem 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 scriptae scriptbisso redefine SIGINTo 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 bashse comporta dessa maneira, talvez registrar um bug com eles? O código sig.cparece 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.

informação relacionada