¿Cuáles son los valores mínimo y máximo de los códigos de salida en Linux?

40

¿Cuáles son los valores mínimos y máximos de los siguientes códigos de salida en Linux:

  1. El código de salida devuelto por un ejecutable binario (por ejemplo: un programa en C).
  2. El código de salida devuelto por un script bash (cuando se llama exit).
  3. El código de salida devuelto por una función (al llamar return). Creo que esto es entre 0y 255.
usuario271801
fuente
Para la parte 3, ¿te refieres a regresar de una función de shell ? Eso puede depender del shell, pero observo que el manual de Bash dice "Los estados de salida caen entre 0 y 255 " y "Los estados de salida de los componentes integrados de shell y los comandos compuestos también están limitados a este rango " .return Por supuesto, es un estado integrado de shell.
Toby Speight
Relacionado (tiene respuestas a la mayoría de sus preguntas): ¿ Código de salida predeterminado cuando finaliza el proceso?
Stéphane Chazelas
@TobySpeight, eso es una limitación del bashshell. Algunos shells como zshpueden devolver cualquier valor firmado de 32 bits como for exit. A algunos les gusta rco espueden devolver datos de cualquiera de los tipos que admiten (escalar o lista). Consulte las preguntas y respuestas vinculadas para más detalles.
Stéphane Chazelas

Respuestas:

74

El número pasado a la llamada _exit()/ exit_group()system (a veces denominado código de salida para evitar la ambigüedad con el estado de salida, que también se refiere a una codificación del código de salida o el número de señal e información adicional dependiendo de si el proceso se cerró o salió normalmente) ) es de tipo int, por lo que en sistemas similares a Unix como Linux, generalmente un número entero de 32 bits con valores de -2147483648 (-2 31 ) a 2147483647 (2 31 -1).

Sin embargo, en todos los sistemas, cuando el proceso padre (o la subreaper niño o initsi el padre murió) utiliza el wait(), waitpid(), wait3(), wait4()las llamadas al sistema para recuperar, sólo los 8 bits inferiores de la misma son disponibles (valores de 0 a 255 (2 8 - 1)).

Cuando se usa la waitid()API (o un controlador de señal en SIGCHLD), en la mayoría de los sistemas (y como POSIX ahora requiere más claramente en la edición 2016 del estándar (ver _exit()especificación )), el número completo está disponible (en el si_statuscampo de la estructura devuelta ) Sin embargo, ese no es el caso en Linux, que también trunca el número a 8 bits con la waitid()API, aunque es probable que eso cambie en el futuro.

En general, solo querrá usar los valores 0 (generalmente significa éxito) a 125 solamente, ya que muchos shells usan valores superiores a 128 en su $?representación del estado de salida para codificar el número de señal de un proceso que se está ejecutando y 126 y 127 para especiales condiciones

Es posible que desee usar 126 a 255 exit()para significar lo mismo que para el shell $?(como cuando lo hace un script ret=$?; ...; exit "$ret"). Usar valores fuera de 0 -> 255 generalmente no es útil. Por lo general, solo haría eso si sabe que el padre usará la waitid()API en sistemas que no se truncan y si necesita el rango de valores de 32 bits. Tenga en cuenta que si lo hace, exit(2048)por ejemplo, los padres lo verán como un éxito utilizando las wait*()API tradicionales .

Más información en:

Es de esperar que las preguntas y respuestas contesten la mayoría de sus otras preguntas y aclaren qué se entiende por estado de salida . Agregaré algunas cosas más:

Un proceso no puede finalizar a menos que se elimine o llame a las llamadas _exit()/ exit_group()system. Cuando regresa de main()in C, la libc llama a esa llamada del sistema con el valor de retorno.

La mayoría de los idiomas tienen una exit()función que envuelve esa llamada del sistema y el valor que toman, si lo hay, generalmente se pasa como es a la llamada del sistema. (tenga en cuenta que, en general, hacen más cosas como la limpieza realizada por la exit()función de C que vacía los buffers estándar, ejecuta los atexit()ganchos ...)

Ese es el caso de al menos:

$ 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)

De vez en cuando ves algunos que se quejan cuando usas un valor fuera de 0-255:

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

Algunos proyectiles se quejan cuando usa un valor negativo:

$ 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 deja el comportamiento indefinido si el valor pasado a la exitconstrucción especial está fuera de 0-> 255.

Algunos proyectiles muestran algunos comportamientos inesperados si lo haces:

  • bash(y mkshno pdkshen el que se basa) se encarga de truncar el valor a 8 bits:

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

    Entonces, en esos shells, si desea salir con un valor fuera de 0-255, debe hacer algo como:

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

    Es decir, ejecutar otro comando en el mismo proceso que puede llamar a la llamada del sistema con el valor que desee.

  • como se mencionó en ese otro Q&A, ksh93tiene el comportamiento más extraño para los valores de salida de 257 a 256 + max_signal_number donde, en lugar de llamar exit_group(), se mata con la señal correspondiente¹.

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

    y de lo contrario trunca el número como bash/ mksh.


¹ Sin embargo, es probable que eso cambie en la próxima versión. Ahora que el desarrollo ksh93se ha asumido como un esfuerzo comunitario fuera de AT&T, ese comportamiento, aunque alentado de alguna manera por POSIX, está siendo revertido

Stéphane Chazelas
fuente
2
¿Sabes si hay alguna discusión sobre la implementación del código de salida completo en si_statusLinux?
Ruslan
2
@Ruslan, no más que austingroupbugs.net/view.php?id=594#c1318 (de Eric Blake (RedHat)) en el enlace que le di
Stéphane Chazelas
1
"es de tipo int, entonces un entero de 32 bits". ¿Linux realmente garantiza que un int siempre será de 32 bits? ¿Incluso cuando se ejecuta en algunos de esos pequeños microcontroladores? Eso me parece realmente extraño. POSIX ciertamente no lo hace.
Voo
@Voo, esos pequeños microcontroladores no pueden ejecutar Linux. Mientras que C requiere inttener al menos 16 bits, POSIX requiere más o menos 32 bits y entornos de programación para tener un uint32_t . No sé si Linux es compatible con algún entorno de programación donde los ints no sean de 32 bits, nunca me he encontrado con ninguno.
Stéphane Chazelas
1
En un sistema operativo compatible con POSIX, puede obtener el código de salida completo de 32 bits en la versión reciente de Bourne Shell, consulte: schillix.sourceforge.net/man/man1/bosh.1.html
schily
12

El mínimo es 0, y eso se considera el valor de éxito. Todos los demás son un fracaso. El máximo 255también se conoce como -1.

Estas reglas se aplican tanto a los scripts como a otros ejecutables, así como a las funciones de shell.

Los valores más grandes dan como resultado el módulo 256.

Tomász
fuente
2
Para ser precisos, en algunos shells tipo Bourne (pero no en bashotros u otros más utilizados) el código de salida pasado al exitincorporado no se trata como módulo-256, y en su lugar causa un error. (Por ejemplo, lo común exit -1no es un equivalente portátil exit 255en la mayoría de los shells). Y si exit(-1)el nivel C es equivalente a exit(255)un detalle que de hecho es seguro que funciona, pero se basa en el comportamiento definido de implementación (aunque esto no es un problema en los sistemas modernos que es probable que use en la práctica).
mtraceur
Por lo que sé, solo ksh93 limita el exit(1)parámetro a 8 bits.
schily
6

Esto parece muy simple, pero ¡ay!

El lenguaje C (y siguiendo que la mayoría de los otros lenguajes directa o indirectamente) requiere que regresar mainsea ​​equivalente a llamar exitcon el mismo argumento que el valor de retorno. Este es un número entero (el tipo de retorno es muy claramente int), por lo que, en principio, la gama sería INT_MINa INT_MAX.

Sin embargo, POSIX establece que solo los 8 bits más bajos pasados exitestarán disponibles para un proceso padre en espera, literalmente como si fuera "estado y 0xFF" .
Entonces, en la práctica, el código de salida es un entero (todavía firmado) del cual solo se establecen los 8 bits más bajos.

El mínimo será -128, y el máximo 127 . Espera, eso no es cierto. Será de 0 a 255.

Pero, por supuesto, no puede ser tan simple . En la práctica, Linux (o más bien bash) lo hace de manera diferente . El rango válido de códigos de retorno es de 0 a 255 (es decir, sin signo).

Para estar en el lado seguro en términos de evitar confusiones, probablemente sea una buena idea asumir que los códigos de retorno no están firmados y emitir todo lo que devuelve waitsin firmar. De esa manera es consistente con lo que ves en un shell. Dado que los bits superiores (incluido el más significativo) se borran, eso ni siquiera es "incorrecto" porque, aunque técnicamente está firmado, los valores reales siempre están sin signo (ya que el bit de signo nunca se establece).
También ayuda a evitar el error común de comparar un código de salida -1, que por alguna extraña razón nunca parece aparecer incluso cuando sale un programa -1(¡bueno, adivina por qué!).

Acerca de su último punto, volviendo de una función, si esta función es main, entonces vea arriba. De lo contrario, depende del tipo de retorno de la función, en principio podría ser cualquier cosa (incluido void).

Damon
fuente
Has tenido razón antes de 1989, cuando waitid()se presentó.
schily
@schily: ¿No estás seguro de lo que quieres decir? waitid()hace exactamente lo mismo, ligeramente diferente. Espera una identificación particular o cualquier hilo, y escribe los resultados en la siginfo_testructura apuntada a donde si_statusestá int(así que ... firmado , igual). Aún así, exit()solo pasa los 8 bits más bajos, así que ... absolutamente lo mismo debajo del capó.
Damon
exit()pasa los 32 bits del parámetro al kernel y waitid()devuelve los 32 bits del código de salida. Tal vez revisó Linux donde a nadie le importa arreglar errores. Si no me cree,
verifíquelo
@schily: Si eso es cierto (no creo que lo sea, pero de todos modos), entonces Linux está roto . Lea la especificación POSIX vinculada a la respuesta de exit, en particular la segunda línea bajo "Descripción" que dice: "aunque solo los 8 bits menos significativos (es decir, estado y 0377) estarán disponibles para un proceso padre en espera " . Así es como funciona una implementación conforme: 8 bits más bajos, no 32. ¿Tiene una referencia para 32 bits que se transmiten?
Damon
Pensé que mencioné que Linux está roto. Peor aún: la gente del kernel de Linux se niega a corregir errores. Si lee el estándar POSIX, encontrará que la versión de 1995 (SUSv1) explica correctamente la característica introducida originalmente por SVr4 en 1989 y que las versiones recientes (por ejemplo, SUSv7tc2) del estándar incluso explican explícitamente eso waitid()y la siginfo_testructura pasada al SIGCHLDcontrolador devuelve todos los 32 bits del exit()parámetro.
schily
2
  1. El código de salida devuelto por un ejecutable binario (por ejemplo: un programa en C).
  2. El código de salida devuelto por un script bash (cuando se llama a exit).

Los códigos de salida de cualquier proceso, ya sea un ejecutable binario, un script de shell o cualquier otra cosa, varían de 0 a 255. Es posible pasar un valor mayor a exit(), pero solo los 8 bits inferiores del estado están disponibles para otros procesos a través wait().

  1. El código de salida devuelto por una función (al llamar a return). Creo que esto está entre 0 y 255.

Se puede declarar que la función de CA devuelve casi cualquier tipo. Los límites de su valor de retorno están determinados completamente por ese tipo: por ejemplo, -128 a 127 para una función que regresa signed char, o 0 a 4.2 mil millones para una función que regresa unsigned int, o cualquier número de punto flotante hasta e incluyendo infpara una función que regresa double. Y eso sin contar los tipos no numéricos, como void *o un struct...

al anochecer
fuente