Por que “Fazer uma saída 130 não é o mesmo que morrer de SIGINT”?

3

Da resposta de Stéphane Chazelas no link

Ideally, we'd want to report to our parent that we died of a SIGINT (so that if it's another bash script for instance, that bash script is also interrupted). Doing an exit 130 is not the same as dying of SIGINT (though some shells will set $? to same value for both cases), however it's often used to report a death by SIGINT (on systems where SIGINT is 2 which is most).

However for bash, ksh93 or FreeBSD sh, that doesn't work. That 130 exit status is not considered as a death by SIGINT and a parent script would not abort there.

  1. Com relação a "Fazer uma saída 130 não é o mesmo que morrer de SIGINT", Quais são as diferenças entre eles?
  2. Por que é que "para bash, ksh93 ou FreeBSD sh, isso não funciona. Isso 130 status de saída não é considerado como uma morte por SIGINT "?

Do manual do Bash:

When a command terminates on a fatal signal whose number is N, Bash uses the value 128+N as the exit status

O número do sinal de SIGINT é 2, então o status de saída de um comando     terminando em SIGINT é 130. Então, parece-me que fazer uma saída 130 é o mesmo que morrer de SIGINT, e que     130 status de saída é considerado como morte pelo SIGINT.

Obrigado.

    
por Tim 18.08.2017 / 04:20

1 resposta

7

O 130 (128 + SIGINT) que você vê em $? após o último comando ter morrido de um SIGINT é uma representação simplificada de seu status de saída feito por alguns shells como bash . Outras shells usarão representações diferentes (como 256 + signum em ksh93, 128 + 256 + signum em yash, representações textuais como sigint ou sigquit+core em rc / es ). Veja Código de saída padrão quando o processo é encerrado? para obter mais detalhes sobre isso.

Um processo pode esperar por seu processo filho e consultar seu status:

  • se foi interrompido (com qual sinal)
  • se foi retomado
  • se foi morto (com qual sinal)
  • se tiver preso (para ptrace d processos)
  • se despejou um núcleo
  • se saiu normalmente com a chamada de sistema _exit() (com qual código de saída)

Para isso, eles usam um dos wait() , waitpid() , waitid() (veja também wait3() , wait4() ) ou um manipulador na chamada do sistema SIGCHLD.

Essas chamadas de sistema retornam todas as informações acima. (Exceto para waitid() em algum sistema, somente os 8 bits mais baixos do número passado para _exit() para o filho que terminam normalmente estão disponíveis).

Mas bash (e a maioria das shells semelhantes a Bourne e csh) agrupa todas essas informações em um número de 8 bits para $? ( $? é os 8 bits mais baixos do código de saída para processos que terminam normalmente , e 128 + signum que foi morto ou suspenso ou preso, todas as outras informações não estão disponíveis). Então, obviamente, há algumas informações sendo perdidas. Em particular, através de $? sozinho, não se pode dizer se um processo fez _exit(130) ou morreu de um SIGINT.

bash sabe quando um processo está sendo morto, obviamente. Por exemplo, quando processos em segundo plano são eliminados, você vê:

[1]+  Interrupt               sleep 20

Mas em $? , ele não fornece informações suficientes para dizer se ele foi morto pelo SIGINT ou se chamou _exit(130) .

Como a maioria dos shells faz essa transformação, os aplicativos sabem melhor do que fazer _exit(number_greater_than_127) para qualquer coisa, mas relatando uma morte por sinal.

Ainda se um processo fizer um _exit(130) , o processo aguardando pelo processo detectará que esse processo terminou normalmente, e não que foi morto por um sinal. Em C, WIFEXITED() retornará verdadeiro , WIFSIGNALED() retornará falso.

bash em si não considerará o processo como tendo morrido de um SIGINT (mesmo que ele pense que pode ter passado por $? contendo o mesmo valor como se tivesse morrido de um SIGINT).

Portanto, isso não acionará a manipulação especial de SIGINT que bash faz. Em um script, tanto bash quanto o comando atualmente em execução no script receberão uma SIGINT sobre ^C (pois ambos estão no mesmo grupo de processos).

bash morre ao receber o SIGINT somente se o comando que ele está esperando também morrer de um SIGINT (a idéia é que se, por exemplo, em seu script, você executar vi ou menos e usar ^ C para abortar algo que não não faça vi / less morrer, seu script não morrer ao retornar para sair do vi / less mais tarde).

Se o comando bash estiver esperando por um _exit(130) em um manipulador de SIGINT, bash não morrerá naquele SIGINT (ele não se considerará como tendo sido interrompido porque não acredita que o filho foi interrompido).

É por isso que quando você quer relatar uma morte por SIGINT , você foi realmente interrompido mesmo que você esteja fazendo algum processamento extra ao receber aquele sinal em um handler , você não deve fazer um _exit(130) , mas realmente se matar com SIGINT (depois de ter restaurado o manipulador padrão para SIGINT). Em um shell, isso é com:

trap '
  extra processing
  trap - INT # restore SIGINT handler
  kill -s INT "$$" # report to the parent that we have indeed been
                   # interrupted
  ' INT
    
por 18.08.2017 / 08:13