Problema estranho com armadilha e SIGINT

5

Por favor, explique isto:

#!/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 , a armadilha não imprime. Se eu mudar de SIGINT para qualquer outro sinal (tentei SIGTERM, SIGUSR1), o trap imprime "Ouch" como esperado. Como isso pode acontecer?

    
por diciotto 06.04.2017 / 18:59

3 respostas

7

If I switch from SIGINT to any other signal (I tried SIGTERM, SIGUSR1) the trap prints “Ouch” as expected.

Aparentemente você não tentou o SIGQUIT; você provavelmente descobrirá que se comporta da mesma forma que SIGINT.

O problema é o controle do trabalho.

Nos primeiros dias do Unix, sempre que o shell colocar um processo ou pipeline em segundo plano, definiu esses processos para ignorar o SIGINT e o SIGQUIT, então eles não seriam encerrados se o usuário subseqüentemente digitado Ctrl + C (interrupção) ou Ctrl + \ (sair) para uma tarefa em primeiro plano. Quando o controle de trabalho surgiu, ele trouxe grupos de processos, e agora tudo que o shell precisa fazer é colocar o trabalho 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 do sinal padrão. Na verdade, provavelmente é necessário que os processos sejam inutilizáveis por Ctrl + C quando eles são trazidos para o primeiro plano.

Mas os shells não interativos não usam controle de job. Faz sentido que um shell não interativo fosse retroceder para o velho 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 os sinais do tipo de teclado sejam enviados para eles. E os scripts de shell são executados em shells não interativos.

E, se você olhar para o último parágrafo sob o comando trap em bash (1) , você verá p>

Signals ignored upon entry to the shell cannot be trapped or reset.

Então, se você executar ./scriptb.sh & do seu prompt de comando do shell interativo , suas disposições de sinal são deixadas em paz (mesmo que esteja sendo colocado em segundo plano), e o comando trap funciona conforme o esperado. Mas, se você executar ./scripta.sh (com ou sem & ), ele executa o script em um shell não interativo. E quando esse shell não interativo executar ./scriptb.sh & , ele define o processo scriptb para ignorar a interrupção e sair. E, portanto, o comando trap em scriptb.sh falha silenciosamente.

    
por 07.04.2017 / 04:06
3

Com alguns traços:

strace -o aaa ./scripta

podemos observar isso 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

, então scripta bloqueou os sinais INT e CHLD ; scriptb herda essas configurações através do fork (aqui chamado clone ). E o que é scriptb fazendo? Se nós o rodarmos de scripta via:

strace -o bbb ./scriptb &

E, em seguida, procurar por coisas relacionadas ao sinal, 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 INT sinais recebem primeiro o tratamento padrão e, em seguida, são ignorados. scriptb executado diretamente do shell em strace mostra por contraste:

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 tratamento repetido da chamada de sono. OK. Uh Vamos colocar uma correção entre scripta e scriptb que redefine SIGINT para 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. No entanto, eu não sei porque o bash se comporta dessa maneira, talvez arquive um bug com eles? O código em sig.c parece muito complicado, e há mais manipulação de sinal em outro lugar ...

    
por 07.04.2017 / 01:43
0

Problema estranho com armadilha e SIGINT

Obrigado a todos pelas respostas e pelo tempo que você levou estudando o problema.

Por favor, permita-me recapitular e integrar (com desculpas pelo que pode ser óbvio no seguinte):

1) Eu esqueci de adicionar a minha pergunta Eu tentei SIGQUIT também e se comportou como SIGINT;

2) A partir disso, eu já suspeitava que o problema estava relacionado à disposição padrão de uma bash interativa para esses dois sinais;

3) Sua ação padrão não acontece ao interagir com o bash é porque não faz sentido desistir ou interromper nada quando a única coisa que você tem é o prompt. Se você quiser deixar o shell, basta digitar exit;

4) Eu 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 fundo (não interativo), (aquele que executa scriptb.sh no nosso caso); / p>

6) De fato, assim como o grupo de processos em primeiro plano não herda (do shell que os iniciou) as disposições para SIGQUIT e SIGINT, IMHO, deve fazer sentido que o mesmo aconteça com os grupos de processos em segundo plano. p>

7) Além disso, qualquer que seja a disposição herdada, armadilha deve mudá-lo.

8) Apesar de tudo, inclino-me a concordar com o thrig e pensar que o que estamos vendo aqui é um bug.

    
por 07.04.2017 / 10:06