Códigos de salida mayores que 255: ¿posibles?
En caso afirmativo, ¿en qué sistema operativo, shell o lo que sea?
Considere el siguiente programa Java (estoy usando Java solo como ejemplo; cualquier lenguaje sería bueno para esta pregunta, que trata más sobre sistemas operativos):
public class ExitCode {
public static void main(String args[]) {
System.exit(Integer.parseInt(args[0]));
}
}
Al ejecutarlo en Linux y bash, siempre devuelve valores menores a 255, por ejemplo ( echo $?
imprime el código de salida del comando ejecutado anteriormente)
> java ExitCode 2; echo $?
2
> java ExitCode 128; echo $?
128
> java ExitCode 255; echo $?
255
> java ExitCode 256; echo $?
0
> java ExitCode 65536; echo $?
0
EDITADO: la mayoría de las respuestas a continuación explican completamente lo que sucede en las variantes de UNIX. Todavía me pregunto acerca de otros sistemas operativos.
Usando wait()
owaitpid()
No es posible en Unix y derivados usar funciones POSIX como wait()
y waitpid()
. La información del estado de salida devuelta consta de dos campos de 8 bits, uno que contiene el estado de salida y el otro que contiene información sobre la causa de la muerte (0 implica una salida ordenada bajo el control del programa, otros valores indican que una señal lo mató e indica si se arrojó un núcleo).
Usando sigaction()
conSA_SIGINFO
Si trabaja duro y lee la especificación POSIX de sigaction()
y <signal.h>
Signal Actions , encontrará que puede obtener el valor de 32 bits que le pasa exit()
un proceso secundario. Sin embargo, no es del todo sencillo.
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
static siginfo_t sig_info = { 0 };
static volatile sig_atomic_t sig_num = 0;
static void *sig_ctxt = 0;
static void catcher(int signum, siginfo_t *info, void *vp)
{
sig_num = signum;
sig_info = *info;
sig_ctxt = vp;
}
static void set_handler(int signum)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);
if (sigaction(signum, &sa, 0) != 0)
{
int errnum = errno;
fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
exit(1);
}
}
static void prt_interrupt(FILE *fp)
{
if (sig_num != 0)
{
fprintf(fp, "Signal %d from PID %d (status 0x%.8X; UID %d)\n",
sig_info.si_signo, (int)sig_info.si_pid, sig_info.si_status,
(int)sig_info.si_uid);
sig_num = 0;
}
}
static void five_kids(void)
{
const int base = 0xCC00FF40;
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid < 0)
break;
else if (pid == 0)
{
printf("PID %d - exiting with status %d (0x%.8X)\n",
(int)getpid(), base + i, base + i);
exit(base + i);
}
else
{
int status = 0;
pid_t corpse = wait(&status);
if (corpse != -1)
printf("Child: %d; Corpse: %d; Status = 0x%.4X - waited\n", pid, corpse, (status & 0xFFFF));
struct timespec nap = { .tv_sec = 0, .tv_nsec = 1000000 }; // 1 millisecond
nanosleep(&nap, 0);
prt_interrupt(stdout);
fflush(0);
}
}
}
int main(void)
{
set_handler(SIGCHLD);
five_kids();
}
Cuando se ejecuta (programa sigexit73
compilado desde sigexit73.c
), esto produce un resultado como:
$ sigexit73
PID 26599 - exiting with status -872349888 (0xCC00FF40)
Signal 20 from PID 26599 (status 0xCC00FF40; UID 501)
Child: 26600; Corpse: 26599; Status = 0x4000 - waited
PID 26600 - exiting with status -872349887 (0xCC00FF41)
Signal 20 from PID 26600 (status 0xCC00FF41; UID 501)
Child: 26601; Corpse: 26600; Status = 0x4100 - waited
PID 26601 - exiting with status -872349886 (0xCC00FF42)
Signal 20 from PID 26601 (status 0xCC00FF42; UID 501)
Child: 26602; Corpse: 26601; Status = 0x4200 - waited
PID 26602 - exiting with status -872349885 (0xCC00FF43)
Signal 20 from PID 26602 (status 0xCC00FF43; UID 501)
Child: 26603; Corpse: 26602; Status = 0x4300 - waited
PID 26603 - exiting with status -872349884 (0xCC00FF44)
Signal 20 from PID 26603 (status 0xCC00FF44; UID 501)
$
Con la llamada de un milisegundo eliminada nanosleep()
, el resultado probablemente se verá así:
$ sigexit73
sigexit23
PID 26621 - exiting with status -872349888 (0xCC00FF40)
Signal 20 from PID 26621 (status 0xCC00FF40; UID 501)
Child: 26622; Corpse: 26621; Status = 0x4000 - waited
PID 26622 - exiting with status -872349887 (0xCC00FF41)
PID 26623 - exiting with status -872349886 (0xCC00FF42)
Signal 20 from PID 26622 (status 0xCC00FF41; UID 501)
Child: 26624; Corpse: 26623; Status = 0x4200 - waited
Signal 20 from PID 26623 (status 0xCC00FF42; UID 501)
Child: 26625; Corpse: 26622; Status = 0x4100 - waited
PID 26624 - exiting with status -872349885 (0xCC00FF43)
PID 26625 - exiting with status -872349884 (0xCC00FF44)
$
Tenga en cuenta que sólo hay tres líneas que comienzan Signal
aquí y también sólo tres líneas que terminan en waited
; algunas de las señales y estados de salida se pierden. Es probable que esto se deba a problemas de sincronización entre las SIGCHLD
señales que se establecen en el proceso principal.
Sin embargo, el punto clave es que se pueden transmitir 4 bytes de datos en el estado exit()
cuando el código usa sigaction()
,, para rastrear el estado.SIGCHLD
SA_SIGINFO
Solo para que conste, la prueba se realizó en una MacBook Pro con macOS Mojave 10.14.6, usando GCC 9.2.0 y XCode 11.3.1. El código también está disponible en mi repositorio SOQ (Stack Overflow Question) en GitHub como archivo sigexit73.c
en el subdirectorio src/so-1843-7779 .
En Windows moderno, el sistema operativo en sí y el shell de la consola predeterminado ( CMD.EXE
) aceptan y muestran códigos de salida en al menos todo el rango de enteros con signo de 32 bits. Al ejecutar el ejemplo anterior se CMD.EXE
obtienen los códigos de salida que solicitó:
> java ExitCode 2
> echo %errorlevel%
2
> java ExitCode 128
> echo %errorlevel%
128
> java ExitCode 255
> echo %errorlevel%
255
> java ExitCode 256
> echo %errorlevel%
256
> java ExitCode 65536
> echo %errorlevel%
65536
Windows realmente no tiene el concepto de señales de Unix, ni intenta secuestrar el código de salida para agregar información adicional, por lo que mientras su shell (o cualquier programa que termine leyendo el código de salida) tampoco lo haga, deberías recuperar los códigos de salida que devolviste. Afortunadamente, los programas que utilizan el tiempo de ejecución C de Microsoft (incluidos todos los programas compilados con MS Visual C++) conservan el código de salida tal cual de los procesos de salida.
Windows tiene muchos más códigos de salida, más de 14.000. (Estoy seguro de que a menudo viste algunos de ellos en tu propia pantalla).
Aquí viene:
- Una lista de códigos de salida de Windows .
- Script de PowerShell para probarlos .