web-dev-qa-db-fra.com

Code de sortie par défaut à la fin du processus?

Lorsqu'un processus est tué avec un signal gérable comme SIGINT ou SIGTERM mais qu'il ne gère pas le signal, quel sera le code de sortie du processus?

Qu'en est-il des signaux non gérables comme SIGKILL?

D'après ce que je peux dire, tuer un processus avec SIGINT entraîne probablement un code de sortie 130, mais cela varierait-il selon le noyau ou la mise en œuvre 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
60
Cory Klein

Les processus peuvent appeler l'appel système _exit() (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 (exception à cela lorsque en utilisant waitid() ou un gestionnaire sur SIGCHLD dans le parent pour récupérer ce code , mais pas sous Linux).

Le parent fera généralement un wait() ou waitpid() pour obtenir le statut de leur enfant sous forme d'entier (bien que waitid() avec une sémantique quelque peu différente peut également être utilisé).

Sous Linux et la plupart des Unices, si le processus s'est terminé normalement, les bits 8 à 15 de ce numéro d'état contiendront le code de sortie transmis à exit(). Si ce n'est pas le cas, 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$? contient par exemple ce nombre défini par waitpid():

$ Perl -e 'system q(kill $$); printf "%04x\n", $?'
000f # killed by signal 15
$ Perl -e 'system q(kill -ILL $$); printf "%04x\n", $?'
0084 # killed by signal 4 and core dumped
$ Perl -e 'system q(exit $((0xabc))); printf "%04x\n", $?'
bc00 # terminated normally, 0xbc the lowest 8 bits of the status

Les shells de type Bourne font également l'état de sortie de la dernière commande exécutée dans leur propre variable $?. Cependant, il ne contient pas directement le nombre renvoyé par waitpid(), mais une transformation sur celui-ci, et c'est différent entre les shells.

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ù il diffère, c'est quand 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 spécifie 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 $? Contiendront le numéro de signal. Mais, où n est le numéro du signal,

  • dans ash, zsh, pdksh, bash, le Bourne Shell, $? est 128 + n. Cela signifie que dans ces shells, si vous obtenez un $? De 129, Vous ne savez pas si c'est parce que le processus s'est terminé avec exit(129) ou s'il a été tué par le signal 1 (HUP sur la plupart des systèmes). Mais la raison est que les shells, lorsqu'ils sortent d'eux-mêmes, retournent par défaut l'état de sortie de la dernière commande quittée. En vous assurant que $? N'est jamais supérieur à 255, cela permet d'avoir un état de sortie cohérent:

    $ bash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    bash: line 1: 16720 Terminated              sh -c "kill \$\$"
    8f # 128 + 15
    $ bash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    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, $? Est 256 + n. Cela signifie qu'à partir d'une valeur de $?, Vous pouvez faire la différence entre un processus tué et non tué. Les versions plus récentes de ksh, à 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 que ksh générera un vidage de mémoire supplémentaire (écrasant potentiellement l'autre) si le processus a été tué par un signal de génération de cœur:

    $ ksh -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    ksh: 16828: Terminated
    10f # 256 + 15
    $ ksh -c 'sh -c "kill -ILL \$\$"; exit'; printf '%x\n' "$?"
    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 bug, c'est que ksh93 Se tue même si $? Vient d'un return 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 offre un compromis. Il renvoie 256 + 128 + n. Cela signifie que nous pouvons également faire la différence entre un processus interrompu et un processus qui s'est terminé correctement. Et à sa sortie, il signalera 128 + n Sans avoir à se suicider ni à subir les effets secondaires qu'il peut avoir.

    $ yash -c 'sh -c "kill \$\$"; printf "%x\n" "$?"'
    18f # 256 + 128 + 15
    $ yash -c 'sh -c "kill \$\$"; exit'; printf '%x\n' "$?"
    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)

Sur les fronts non Bourne:

  • csh/tcsh et fish identiques au Bourne Shell, sauf que l'état est dans $status au lieu de $? (notez que zsh définit également $status pour la compatibilité avec csh (en plus de $?)).
  • rc: le statut de sortie est également dans $status, mais lorsqu'elle est tuée par un signal, cette variable contient le nom du signal (comme sigterm ou sigill+core si un noyau a été généré) au lieu d'un nombre, ce qui est une autre preuve de la bonne conception de ce Shell.
  • es. l'état 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 ou sigsegv+core comme dans rc.

Pour être complet, nous devrions mentionner les tableaux $pipestatus De zsh et $PIPESTATUS De bash qui contiennent l'état de sortie des composants du dernier pipeline.

Et également pour être complet, en ce qui concerne les fonctions Shell et les fichiers sources, par défaut, les fonctions retournent avec l'état de sortie de la dernière exécution de la commande, mais peuvent également définir un état de retour de manière explicite avec la fonction intégrée return. Et nous voyons quelques différences ici:

  • bash et mksh (puisque R41, ne régression ^ Wchange apparemment introduite intentionnellement ) tronquera le nombre (positif ou négatif) à 8 bits. Ainsi, par exemple, return 1234 Définira $? Sur 210, return -- -1 Définira $? Sur 255.
  • zsh et pdksh (et les dérivés autres que mksh) autorisent tout entier décimal 32 bits signé (-231 à 231-1) (et tronquer le nombre à 32 bits).
  • ash et yash autorisent tout entier positif de 0 à 231-1 et renvoie une erreur pour tout nombre en dehors de cela.
  • ksh93 Pour return 0 À return 320 Définissez $? Tel quel, mais pour toute autre chose, tronquez à 8 bits. Attention, comme déjà mentionné, le fait de renvoyer un nombre compris entre 256 et 320 pourrait entraîner la mort de ksh lors de sa sortie.
  • rc et es permettent de renvoyer n'importe quoi 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 le statut de sortie d'un processus, comme 127 Ou 126 pour commande non trouvée ou non exécutable (ou erreur de syntaxe dans un fichier source) ...

64

Lorsqu'un processus se termine, il renvoie une valeur entière au système d'exploitation. Sur la plupart des variantes Unix, cette valeur est prise modulo 256: tout sauf les bits de poids faible est ignoré. Le statut d'un processus enfant est renvoyé à son parent via un entier 16 bits dans lequel

  • les bits 0 à 6 (les 7 bits de poids faible) sont le numéro de signal qui a été utilisé pour tuer le processus, ou 0 si le processus s'est terminé normalement;
  • le bit 7 est positionné si le processus a été tué par un signal et un noyau vidé;
  • les bits 8 à 15 sont le code de sortie du processus si le processus s'est terminé normalement, ou 0 si le processus a été tué par un signal.

Le statut est retourné par l'appel système wait ou l'un de ses frères et sœurs. POSIX ne spécifie pas l'encodage exact de l'état de sortie et du numéro de signal; il fournit seulement

  • un moyen de savoir si l'état de sortie correspond à un signal ou à une sortie normale;
  • un moyen d'accéder au code de sortie, si le processus s'est terminé normalement;
  • un moyen d'accéder au numéro de signal, si le processus a été tué par un signal.

À strictement parler, il n'y a pas de code de sortie lorsqu'un processus est tué par un signal: ce qu'il y a à la place est une sortie état .

Dans un script Shell, le état de sortie d'une commande est signalé via la variable spéciale $? . Cette variable code le statut de sortie de manière ambiguë:

  • Si le processus s'est terminé normalement, alors $? est son état de sortie.
  • Si le processus a été interrompu par un signal, alors $? est 128 plus le numéro de signal sur la plupart des systèmes. POSIX exige uniquement que $? est supérieur à 128 dans ce cas; ksh93 ajoute 256 au lieu de 128. Je n'ai jamais vu de variante Unix qui ait fait autre chose que d'ajouter une constante au numéro de signal.

Ainsi, dans un script Shell, vous ne pouvez pas dire avec certitude si une commande a été tuée par un signal ou sortie avec un code d'état supérieur à 128, sauf avec ksh93. Il est très rare que des programmes se terminent avec des codes d'état supérieurs à 128, en partie parce que les programmeurs l'évitent en raison de $? ambiguïté.

SIGINT est le signal 2 sur la plupart des variantes Unix, donc $? est 128 + 2 = 130 pour un processus qui a été tué par SIGINT. Vous verrez 129 pour SIGHUP, 137 pour SIGKILL, etc.

Cela dépend de votre Shell. Depuis la page de manuel bash(1), Shell GRAMMAR section, Simple Commands sous-section:

La valeur de retour d'une commande simple est [...] 128 + n si la commande se termine par le signal n .

Puisque SIGINT sur votre système est le signal numéro 2, la valeur de retour est 130 lorsqu'il est exécuté sous Bash.

10

Cela semble être le bon endroit pour mentionner que SVr4 a introduit waitid () en 1989, mais aucun programme important ne semble l'utiliser jusqu'à présent. waitid () permet de récupérer les 32 bits complets du code exit ().

Il y a environ 2 mois, j'ai réécrit la partie de contrôle d'attente/travail du Bourne Shell pour utiliser waitid () au lieu de waitpid (). Cela a été fait afin de supprimer la limitation qui masque le code de sortie avec 0xFF.

L'interface waitid () est beaucoup plus propre que les implémentations précédentes de wait () à l'exception de l'appel cwait () de UNOS de 1980.

Vous pouvez être intéressé à lire la page de manuel sur:

http://schillix.sourceforge.net/man/man1/bosh.1.html

et vérifiez la section "Substitution de paramètres" actuellement en page 8.

Les nouvelles variables .sh. * Ont été introduites pour l'interface waitid (). Cette interface n'a plus de signification ambiguë pour les nombres connus pour $? et rendre l'interfaçage beaucoup plus facile.

Notez que vous devez avoir un waitid () compatible POSIX pour pouvoir utiliser cette fonctionnalité, donc Mac OS X et Linux ne le proposent pas actuellement, mais le waitid () est émulé lors de l'appel de waitpid (), ainsi de suite un plate-forme non-POSIX, vous n'obtiendrez toujours que 8 bits du code de sortie.

En bref: .sh.status est le code de sortie numérique, .sh.code est la raison de sortie numérique.

Pour une meilleure portabilité, il existe: .sh.codename pour la version textuelle du motif de sortie, par ex. "DUMPED" et .sh.termsig, le nom unique du signal qui a mis fin au processus.

Pour une meilleure utilisation, il existe deux valeurs .sh.codename non liées à la sortie: "NOEXEC" et "NOTFOUND" qui sont utilisées lorsqu'un programme ne peut pas être lancé du tout.

FreeBSD a corrigé son bogue kerlnel waitid () dans les 20 heures suivant mon rapport, Linux n'a pas encore commencé avec son correctif. J'espère que 26 ans après avoir introduit cette fonctionnalité qui est maintenant dans POSIX, tous les OS la prendront en charge rapidement.

3
schily