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