Lorsqu'un processus est tué avec un signal gérable comme SIGINT
ou SIGTERM
mais il ne gère pas le signal, quel sera le code de sortie du processus ?
Qu'en est-il des signaux impossibles à gérer comme SIGKILL
?
D'après ce que je peux dire, tuer un processus avec SIGINT
entraîne probablement le code de sortie 130
, mais cela varierait-il selon l'implémentation du noyau ou du shell ?
$ cat myScript
#!/bin/bash
sleep 5
$ ./myScript
<ctrl-c here>
$ echo $?
130
Je ne sais pas comment je testerais les autres signaux…
$ ./myScript &
$ killall myScript
$ echo $?
0 # duh, that's the exit code of killall
$ killall -9 myScript
$ echo $?
0 # same problem
Réponse acceptée :
Les processus peuvent appeler le _exit()
appel système (sous Linux, voir aussi exit_group()
) avec un argument entier pour signaler un code de sortie à leur parent. Bien qu'il s'agisse d'un entier, seuls les 8 bits les moins significatifs sont disponibles pour le parent (à l'exception de l'utilisation de waitid()
ou gestionnaire sur SIGCHLD dans le parent pour récupérer ce code, mais pas sur Linux).
Le parent fera généralement un wait()
ou waitpid()
pour obtenir le statut de leur enfant sous la forme d'un entier (bien que waitid()
avec une sémantique quelque peu différente peut également être utilisée).
Sous Linux et la plupart des Unix, si le processus s'est terminé normalement, les bits 8 à 15 de cet statut number contiendra le code de sortie tel qu'il est passé à exit()
. Sinon, les 7 bits les moins significatifs (0 à 6) contiendront le numéro de signal et le bit 7 sera défini si un cœur a été vidé.
perl
est $?
par exemple contient ce nombre tel que défini par waitpid()
:
$ perl -e 'system q(kill $$); printf "%04xn", $?'
000f # killed by signal 15
$ perl -e 'system q(kill -ILL $$); printf "%04xn", $?'
0084 # killed by signal 4 and core dumped
$ perl -e 'system q(exit $((0xabc))); printf "%04xn", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status
Les shells de type Bourne font également le statut de sortie de la dernière commande d'exécution dans leur propre $?
variable. Cependant, il ne contient pas directement le nombre retourné par waitpid()
, mais une transformation dessus, et c'est différent entre les coques.
Ce qui est commun à tous les shells, c'est que $?
contient les 8 bits les plus bas du code de sortie (le nombre passé à exit()
) si le processus s'est terminé normalement.
Là où cela diffère, c'est lorsque le processus se termine par un signal. Dans tous les cas, et c'est requis par POSIX, le nombre sera supérieur à 128. POSIX ne précise pas quelle peut être la valeur. En pratique cependant, dans tous les shells de type Bourne que je connais, les 7 bits les plus bas de $?
contiendra le numéro de signal. Mais, où n
est le numéro du signal,
-
dans ash, zsh, pdksh, bash, le shell Bourne,
$?
est128 + n
. Cela signifie que dans ces shells, si vous obtenez un$?
de129
, vous ne savez pas si c'est parce que le processus s'est terminé avecexit(129)
ou s'il a été tué par le signal1
(HUP
sur la plupart des systèmes). Mais la logique est que les shells, lorsqu'ils se quittent eux-mêmes, renvoient par défaut le statut de sortie de la dernière commande sortie. En vous assurant que$?
n'est jamais supérieur à 255, ce qui permet d'avoir un statut de sortie cohérent :$ bash -c 'sh -c "kill $$"; printf "%xn" "$?"' bash: line 1: 16720 Terminated sh -c "kill $$" 8f # 128 + 15 $ bash -c 'sh -c "kill $$"; exit'; printf '%xn' "$?" bash: line 1: 16726 Terminated sh -c "kill $$" 8f # here that 0x8f is from a exit(143) done by bash. Though it's # not from a killed process, that does tell us that probably # something was killed by a SIGTERM
-
ksh93
,$?
est256 + n
. Cela signifie qu'à partir d'une valeur de$?
vous pouvez faire la différence entre un processus tué et non tué. Versions plus récentes deksh
, à la sortie, si$?
était supérieur à 255, se tue avec le même signal afin de pouvoir signaler le même état de sortie à son parent. Bien que cela semble être une bonne idée, cela signifie queksh
générera un core dump supplémentaire (écrasant potentiellement l'autre) si le processus a été tué par un signal de génération de core :$ ksh -c 'sh -c "kill $$"; printf "%xn" "$?"' ksh: 16828: Terminated 10f # 256 + 15 $ ksh -c 'sh -c "kill -ILL $$"; exit'; printf '%xn' "$?" ksh: 16816: Illegal instruction(coredump) Illegal instruction(coredump) 104 # 256 + 15, ksh did indeed kill itself so as to report the same # exit status as sh. Older versions of `ksh93` would have returned # 4 instead.
Là où vous pourriez même dire qu'il y a un bogue, c'est que
ksh93
se tue même si$?
provient d'unreturn 257
fait par une fonction :$ ksh -c 'f() { return "$1"; }; f 257; exit' zsh: hangup ksh -c 'f() { return "$1"; }; f 257; exit' # ksh kills itself with a SIGHUP so as to report a 257 exit status # to its parent
-
yash
.yash
propose un compromis. Elle renvoie256 + 128 + n
. Cela signifie que nous pouvons également faire la différence entre un processus tué et un processus qui s'est terminé correctement. Et à la sortie, il rapportera128 + n
sans avoir à se suicider et les effets secondaires que cela peut avoir.$ yash -c 'sh -c "kill $$"; printf "%xn" "$?"' 18f # 256 + 128 + 15 $ yash -c 'sh -c "kill $$"; exit'; printf '%xn' "$?" 8f # that's from a exit(143), yash was not killed
Pour obtenir le signal à partir de la valeur de $?
, la méthode portable consiste à utiliser kill -l
:
$ /bin/kill 0
Terminated
$ kill -l "$?"
TERM
(pour la portabilité, vous ne devez jamais utiliser de numéros de signaux, uniquement des noms de signaux)
Connexe :sauvegardes au niveau octet ou au niveau fichier ?Sur les fronts autres que Bourne :
csh
/tcsh
etfish
identique au shell Bourne sauf que le statut est dans$status
au lieu de$?
(notez quezsh
définit également$status
pour la compatibilité aveccsh
(en plus de$?
)).rc
:le statut de sortie est dans$status
également, mais lorsqu'elle est tuée par un signal, cette variable contient le nom du signal (commesigterm
ousigill+core
si un noyau a été généré) au lieu d'un nombre, ce qui est encore une autre preuve de la bonne conception de ce shell.-
es
. le statut de sortie n'est pas une variable. Si vous vous en souciez, vous exécutez la commande en tant que :status = <={cmd}
qui renverra un nombre ou
sigterm
ousigsegv+core
comme dansrc
.
Peut-être que pour être complet, nous devrions mentionner zsh
c'est $pipestatus
et bash
$PIPESTATUS
des tableaux contenant l'état de sortie des composants du dernier pipeline.
Et aussi pour être complet, en ce qui concerne les fonctions shell et les fichiers sourcés, par défaut, les fonctions retournent avec le statut de sortie de la dernière commande exécutée, mais peuvent également définir explicitement un statut de retour avec le return
intégré. Et nous voyons quelques différences ici :
bash
etmksh
(depuis R41, une régression^Wchange apparemment introduite intentionnellement) tronquera le nombre (positif ou négatif) à 8 bits. Ainsi par exemplereturn 1234
définira$?
à210
,return -- -1
définira$?
à 255.zsh
etpdksh
(et dérivés autres quemksh
) autorise tout entier décimal signé de 32 bits (-2 à 2-1) (et tronque le nombre à 32 bits).ash
etyash
autoriser tout entier positif de 0 à 2-1 et renvoyer une erreur pour tout nombre en dehors de cela.ksh93
pourreturn 0
pourreturn 320
définir$?
tel quel, mais pour toute autre chose, tronquer à 8 bits. Attention, comme déjà mentionné, le retour d'un nombre compris entre 256 et 320 pourrait causerksh
se suicider en sortant.rc
etes
autoriser le renvoi de tout, même des listes.
Notez également que certains shells utilisent également des valeurs spéciales de $?
/$status
pour signaler certaines conditions d'erreur qui ne sont pas l'état de sortie d'un processus, comme 127
ou 126
pour commande introuvable ou non exécutable (ou erreur de syntaxe dans un fichier sourcé)…