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