Quelles sont les valeurs minimales et maximales des codes de sortie sous Linux?

40

Quelles sont les valeurs min et max des codes de sortie suivants sous Linux:

  1. Le code de sortie renvoyé par un exécutable binaire (par exemple: un programme C).
  2. Le code de sortie renvoyé par un script bash (lors de l'appel exit).
  3. Le code de sortie renvoyé par une fonction (lors de l'appel return). Je pense que c'est entre 0et 255.
utilisateur271801
la source
Pour la partie 3, voulez-vous dire revenir d'une fonction shell ? Cela peut dépendre du shell, mais je remarque que le manuel de Bash dit " Les états de sortie se situent entre 0 et 255 " et " Les statuts de sortie des commandes intégrées au shell et les commandes composées sont également limités à cette plage. " returnEst, bien sûr, un shell intégré.
Toby Speight le
Connexes (réponses à la plupart de vos questions): Code de sortie par défaut lorsque le processus est terminé?
Stéphane Chazelas
@TobySpeight, c'est une limitation du bashshell. Certains autres shells, par exemple, zshpeuvent renvoyer n'importe quelle valeur 32 bits signée, comme pour exit. Certains aiment rcou espeuvent renvoyer des données de n'importe quel type pris en charge (scalaire ou liste). Voir le Q & A lié pour plus de détails.
Stéphane Chazelas

Réponses:

74

Numéro transmis à l' appel système _exit()/ exit_group()(parfois appelé code de sortie pour éviter toute ambiguïté avec l' état de sortie, qui fait également référence à un codage du code de sortie ou du numéro de signal et d'informations supplémentaires, selon que le processus a été arrêté ou arrêté normalement. ) est de type int, donc sur les systèmes de type Unix tels que Linux, généralement un entier 32 bits avec des valeurs comprises entre -2147483648 (-2 31 ) et 2147483647 (2 31 -1).

Cependant, sur tous les systèmes, lorsque le processus parent (ou le subreaper des enfants ou initsi le parent est mort) utilise les wait(), waitpid(), wait3(), wait4()appels système pour le récupérer, seuls les 8 bits inférieurs de celui - ci sont disponibles (valeurs 0 à 255 (2 8 - 1)).

Lors de l'utilisation de l' waitid()API (ou d'un gestionnaire de signal sur SIGCHLD), sur la plupart des systèmes (et comme POSIX l'exige maintenant plus clairement dans l'édition 2016 de la norme (voir _exit()spécification )), le numéro complet est disponible (dans le si_statuschamp de la structure renvoyée). ). Ce n’est pas encore le cas sous Linux, qui tronque également le nombre à 8 bits avec l’ waitid()API, bien que cela puisse changer à l’avenir.

En règle générale, vous ne souhaitez utiliser que les valeurs comprises entre 0 (généralement synonyme de succès) et 125 uniquement, car de nombreux shells utilisent des valeurs supérieures à 128 dans leur $?représentation de l' état de sortie pour coder le numéro de signal d'un processus en cours de suppression et 126 et 127 pour des opérations spéciales. conditions.

Vous voudrez peut-être utiliser entre 126 et 255 exit()pour signifier la même chose que pour le shell $?(comme lorsqu'un script le fait ret=$?; ...; exit "$ret"). L'utilisation de valeurs autres que 0 -> 255 n'est généralement pas utile. Vous ne le ferez généralement que si vous savez que le parent utilisera l' waitid()API sur des systèmes qui ne tronquent pas et que vous avez besoin de la plage de valeurs 32 bits. Notez que si vous faites un exit(2048)exemple, cela sera vu comme un succès par les parents utilisant les wait*()API traditionnelles .

Plus d'infos sur:

J'espère que cette question-réponse devrait répondre à la plupart de vos autres questions et clarifier ce que l'on entend par statut de sortie . J'ajouterai quelques petites choses:

Un processus ne peut pas se terminer s'il n'est pas tué ou s'il appelle les appels _exit()/ exit_group()system. Lorsque vous revenez de main()dans C, les appels libc appel système avec la valeur de retour.

La plupart des langues ont une exit()fonction qui encapsule cet appel système et la valeur qu'elles prennent, le cas échéant, est généralement passée telle quelle à l'appel système. (notez que ceux-ci font généralement plus de choses comme le nettoyage effectué par la exit()fonction de C qui vide les tampons stdio, exécute les atexit()crochets ...)

C'est le cas d'au moins:

$ strace -e exit_group awk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ strace -e exit_group mawk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ strace -e exit_group busybox awk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ echo | strace -e exit_group sed 'Q1234'
exit_group(1234)                        = ?
$ strace -e exit_group perl -e 'exit(1234)'
exit_group(1234)                        = ?
$ strace -e exit_group python -c 'exit(1234)'
exit_group(1234)                        = ?
$ strace -e exit_group expect -c 'exit 1234'
exit_group(1234)                        = ?
$ strace -e exit_group php -r 'exit(1234);'
exit_group(1234)                        = ?
$ strace -e exit_group zsh -c 'exit 1234'
exit_group(1234)

Vous en voyez parfois qui se plaignent lorsque vous utilisez une valeur en dehors de 0-255:

$ echo 'm4exit(1234)' | strace -e exit_group m4
m4:stdin:1: exit status out of range: `1234'
exit_group(1)                           = ?

Certains coquillages se plaignent lorsque vous utilisez une valeur négative:

$ strace -e exit_group dash -c 'exit -1234'
dash: 1: exit: Illegal number: -1234
exit_group(2)                           = ?
$ strace -e exit_group yash -c 'exit -- -1234'
exit: `-1234' is not a valid integer
exit_group(2)                           = ?

POSIX laisse le comportement indéfini si la valeur transmise à la commande exitspéciale spéciale est en dehors de 0-> 255.

Certains coquillages présentent des comportements inattendus:

  • bash(et mkshmais pas pdkshsur lequel il est basé) se charge de tronquer la valeur à 8 bits:

    $ strace -e exit_group bash -c 'exit 1234'
    exit_group(210)                         = ?
    

    Donc, dans ces shells, si vous voulez sortir avec une valeur en dehors de 0-255, vous devez faire quelque chose comme:

    exec zsh -c 'exit -- -12345'
    exec perl -e 'exit(-12345)'
    

    C'est exécuter une autre commande dans le même processus qui peut appeler l'appel système avec la valeur souhaitée.

  • comme mentionné dans cet autre Q & A, ksh93a le comportement le plus étrange pour les valeurs de sortie de 257 à 256 + max_signal_number où au lieu d'appeler exit_group(), il se tue avec le signal correspondant¹.

    $ ksh -c 'exit "$((256 + $(kill -l STOP)))"'
    zsh: suspended (signal)  ksh -c 'exit "$((256 + $(kill -l STOP)))"'
    

    et sinon, tronque le nombre comme bash/ mksh.


¹ Cela risque toutefois de changer dans la prochaine version. Maintenant que le développement de ksh93a été repris en tant qu'effort communautaire en dehors d'AT & T, ce comportement, même s'il a été encouragé par POSIX, est en train d'être annulé.

Stéphane Chazelas
la source
2
Savez-vous s'il y a des discussions sur l'implémentation du code de sortie complet sous si_statusLinux?
Ruslan
2
@Ruslan, pas plus que le austingroupbugs.net/view.php?id=594#c1318 (d'Eric Blake (RedHat)) sur le lien que j'ai donné
Stéphane Chazelas le
1
"est de type int, donc un entier de 32 bits". Linux garantit vraiment qu'un int sera toujours en 32 bits? Même lorsque vous utilisez certains de ces microcontrôleurs minuscules? Cela me semble vraiment étrange. POSIX certainement pas.
Voo le
@Voo, ces microcontrôleurs minuscules ne peuvent pas utiliser Linux. Bien que C exige intau moins 16 bits, POSIX exige au moins 32 bits et les environnements de programmation un uint32_t . Je ne sais pas si Linux prend en charge un environnement de programmation où les entrées sont tout sauf 32 bits, je n'en ai jamais rencontré.
Stéphane Chazelas
1
Sur un système d'exploitation compatible POSIX, vous pouvez obtenir le code de sortie 32 bits complet dans la version récente de Bourne Shell, voir: schillix.sourceforge.net/man/man1/bosh.1.html
schily
12

Le minimum est 0, et c'est considéré la valeur de succès. Tous les autres sont des échecs. Le maximum est 255également appelé -1.

Ces règles s'appliquent à la fois aux scripts et aux autres exécutables, ainsi qu'aux fonctions du shell.

Des valeurs plus grandes résultent en modulo 256.

Tomasz
la source
2
Pour être précis, dans certains shells de type Bourne (mais pas bashou d'autres plus couramment utilisés), le code de sortie transmis à la commande exitintégrée n'est pas traité comme modulo-256, mais provoque plutôt une erreur. (Par exemple, le commun exit -1n’est en réalité pas un équivalent portable exit 255dans la plupart des coques). Et si exit(-1)le niveau C est équivalent à exit(255)un détail dont le fonctionnement est de facto certain, mais qui repose sur un comportement défini par la mise en œuvre (bien que cela ne soit pas un problème sur les systèmes modernes, vous pourrez probablement l'utiliser en pratique).
mtraceur
D'après ce que je sais, seul ksh93 limite le exit(1)paramètre à 8 bits.
Schily
6

Cela a l'air si simple, mais oh les malheurs.

Le langage C (et suivant la plupart des autres langages directement ou indirectement) exige que le renvoi de mainsoit équivalent à un appel exitavec le même argument que la valeur de retour. Ceci est un entier (le type de retour est très clair int), donc en principe, la plage serait INT_MINto INT_MAX.

Cependant, POSIX précise que seuls les 8 bits les plus bas passés exitdoivent être mis à la disposition d'un processus parent en attente, littéralement comme s'il s'agissait de "status & 0xFF" .
Ainsi, en pratique, le code de sortie est un entier (encore signé) dont seuls les 8 bits les plus bas sont définis.

Le minimum sera donc de -128 et le maximum de 127 . Accrochez-vous, ce n'est pas vrai. Ce sera 0 à 255.

Mais hélas, bien sûr, cela ne peut pas être aussi simple . En pratique, Linux (ou plutôt bash) le fait différemment . La plage de codes retour valide est comprise entre 0 et 255 (c.-à-d. Non signé).

Pour éviter toute confusion, il est probablement judicieux de supposer que les codes de retour ne sont pas signés et de renvoyer tout ce que vous renvoyez waiten non signé. De cette façon, cela correspond à ce que vous voyez dans un shell. Etant donné que les bits les plus hauts (y compris le plus significatif) sont effacés, ce n'est même pas "faux" car, bien que techniquement signés, les valeurs réelles sont toujours non signées (car le bit de signe n'est jamais défini).
Cela permet également d'éviter l'erreur courante de comparer un code de sortie à -1, qui pour une raison étrange ne semble jamais apparaître même lorsqu'un programme se termine avec -1(eh bien, devinez pourquoi!).

A propos de votre dernier point, en revenant d’une fonction, si cette fonction se trouve être main, voir ci-dessus. Sinon, cela dépend du type de retour de la fonction, il pourrait en principe être n'importe quoi (y compris void).

Damon
la source
Vous aviez raison avant 1989, date de waitid()son introduction.
Schily
@schily: Vous ne savez pas ce que vous voulez dire? waitid()fait exactement la même chose, légèrement différemment. Il attend un identifiant particulier ou un fil, et écrit les résultats dans la siginfo_tstructure pointée vers où si_statusest int(donc ... signé , de la même manière). Reste exit()que ne passe que les 8 bits les plus bas, donc ... absolument la même chose sous le capot.
Damon
exit()transmet tous les 32 bits du paramètre au noyau et waitid()renvoie tous les 32 bits du code de sortie. Peut-être avez-vous vérifié sur Linux où personne ne se soucie de corriger les bogues. Si vous ne me croyez pas, vérifiez-le sur un système d'exploitation compatible POSIX ...
schily
@schily: Si c'est vrai (je ne pense pas que ce soit le cas, mais quand même), alors Linux est en panne . Veuillez lire la spécification POSIX liée à la réponse de exit, en particulier la deuxième ligne sous "Description" qui indique: "bien que seuls les 8 bits les moins significatifs (c'est-à-dire, statut & 0377) soient disponibles pour un processus parent en attente " . C’est ainsi qu’une implémentation conforme fonctionne: 8 bits au plus bas, et non 32. Avez-vous une référence pour 32 bits transmis?
Damon
Je pensais avoir mentionné que Linux était en panne. Pire encore: les utilisateurs du noyau Linux refusent de corriger les bogues. Si vous lisez le standard POSIX, vous constaterez que la version 1995 (SUSv1) explique correctement la fonctionnalité introduite à l'origine par SVr4 en 1989 et que les versions récentes (par exemple, SUSv7tc2) expliquent même explicitement cela waitid()et la siginfo_tstructure transmise au SIGCHLDgestionnaire. tous les 32 bits du exit()paramètre.
Schily
2
  1. Le code de sortie renvoyé par un exécutable binaire (par exemple: un programme C).
  2. Le code de sortie renvoyé par un script bash (lors de l'appel de exit).

Les codes de sortie de tout processus (qu’il s’agisse d’un exécutable binaire, d’un script shell ou de tout autre procédé) vont de 0 à 255. Il est possible de passer une valeur plus grande à exit(), mais seuls les 8 bits les plus bas de l’état sont rendus disponibles. d'autres processus à travers wait().

  1. Le code de sortie renvoyé par une fonction (lors de l'appel retour). Je pense que c'est entre 0 et 255.

La fonction AC peut être déclarée comme renvoyant presque n'importe quel type. Les limites de sa valeur de retour sont entièrement déterminées par ce type: par exemple, -128 à 127 pour une fonction renvoyée signed char, 0 à 4,2 milliards pour une fonction renvoyée unsigned int, ou tout nombre à virgule flottante allant jusqu'à et y compris infpour une fonction renvoyée double. Et cela ne compte pas les types non numériques, comme void *ou un struct...

crépuscule
la source