¿Cuál es la diferencia entre exit () y abort ()?

130

En C y C ++, ¿cuál es la diferencia entre exit()y abort()? Estoy tratando de finalizar mi programa después de un error (no es una excepción).

Martin Liversage
fuente

Respuestas:

116

abort()sale de su programa sin llamar atexit()primero a las funciones registradas , y sin llamar primero a los destructores de objetos. exit()hace ambas cosas antes de salir de su programa. Sin embargo, no llama a destructores para objetos automáticos. Entonces

A a;
void test() { 
    static A b;
    A c;
    exit(0);
}

Destruirá ay bcorrectamente, pero no llamará destructores de c. abort()no llamaría destructores de ninguno de los objetos. Como esto es desafortunado, el estándar C ++ describe un mecanismo alternativo que garantiza la terminación adecuada:

Los objetos con duración de almacenamiento automático se destruyen en un programa cuya función main()no contiene objetos automáticos y ejecuta la llamada a exit(). El control se puede transferir directamente a tal main()mediante una excepción atrapada main().

struct exit_exception { 
   int c; 
   exit_exception(int c):c(c) { } 
};

int main() {
    try {
        // put all code in here
    } catch(exit_exception& e) {
        exit(e.c);
    }
}

En lugar de llamar exit(), organice ese código en su throw exit_exception(exit_code);lugar.

Johannes Schaub - litb
fuente
2
+1 porque, aunque Brian R. Bondy era bueno, planteaste el problema de abortar / salir (no destructor de los objetos de pila llamados), y ofreciste la alternativa para un proceso C ++ intensivo en RAII.
paercebal el
¡Estaba buscando la manera de salir de un programa sin llamar a dtor y su respuesta es justo lo que estaba buscando! Gracias
acemtp
Eso es perfectamente correcto, por supuesto, si realmente importa que sus destructores automáticos de objetos no se llamen :-)
Chris Huang-Leaver
Que yo sepa, una diferencia más entre salir y abortar sería que el aborto podría (dependiendo de la configuración del sistema operativo) conducir a la generación de un volcado de núcleo.
Dirk Herrmann
33

abort envía una señal SIGABRT, la salida solo cierra la aplicación y realiza una limpieza normal.

Puede manejar una señal de aborto como lo desee, pero el comportamiento predeterminado es cerrar la aplicación también con un código de error.

abort no realizará la destrucción de objetos de sus miembros estáticos y globales, pero la salida sí.

Por supuesto, sin embargo, cuando la aplicación esté completamente cerrada, el sistema operativo liberará cualquier memoria no liberada y otros recursos.

En tanto aborto y la salida de la terminación del programa (asumiendo que no anular el comportamiento por defecto), el código de retorno será devuelto al proceso padre que se inició su aplicación.

Vea el siguiente ejemplo:

SomeClassType someobject;

void myProgramIsTerminating1(void)
{
  cout<<"exit function 1"<<endl;
}

void myProgramIsTerminating2(void)
{
  cout<<"exit function 2"<<endl;
}

int main(int argc, char**argv)
{
  atexit (myProgramIsTerminating1);
  atexit (myProgramIsTerminating2);
  //abort();
  return 0;
}

Comentarios:

  • Si abort no está comentado: no se imprime nada y no se llamará al destructor de algún objeto.

  • Si se comenta abortar como se indicó anteriormente: se llamará a algún destructor de objetos, obtendrá el siguiente resultado:

función de
salida 2 función de salida 1

Brian R. Bondy
fuente
Aquí, llamó a la función de salida 2 y luego a la función de salida 1. gcc 4, Linux 2.6.
extraño
1
La página de manual para atexit dice: "Las funciones [registradas usando atexit] se llaman en orden inverso; no se pasan argumentos".
extraño
@strager tiene razón, se supone que las funciones registradas por atexit se invocan en orden inverso cuando se llama a exit o se devuelve main.
Robert Gamble
Ejecutó una prueba y parece que los destructores de instancias globales se llaman después de todas las devoluciones de llamada atexit.
extraño
+1 para recordar a las personas que el sistema operativo eventualmente liberará todos los recursos asignados incluso después de una llamada abortar ().
Fingolfin
10

Las siguientes cosas suceden cuando un programa llama exit():

  • Las funciones registradas por la atexitfunción se ejecutan
  • Todos los flujos abiertos se vacían y se cierran, los archivos creados por tmpfilese eliminan
  • El programa termina con el código de salida especificado para el host

La abortfunción () envía la SIGABRTseñal al proceso actual; si no se detecta, el programa finaliza sin garantía de que los flujos abiertos se vacíen / cierren o que tmpfilese eliminen los archivos temporales creados mediante, no se invoquen atexitlas funciones registradas y el estado de salida cero se devuelve al host.

Robert Gamble
fuente
hmm el estándar dice que el programa solo no termina si el controlador de señal "no regresa". estás bastante bien con C. ¿te imaginas algún escenario que permita continuar la ejecución normal sin volver? Me imagino longjmp, pero no estoy seguro de cómo se comporta en los manejadores de señales.
Johannes Schaub - litb
En general, llamar a longjmp desde un controlador de señal no está definido, pero hay un caso especial para cuando la señal se generó con elevar / cancelar, así que supongo que esto sería teóricamente posible, aunque no creo haberlo visto nunca. Ahora voy a tener que probarlo;)
Robert Gamble
1
Esto parece funcionar (dividido en varias publicaciones debido al límite de 300 caracteres): #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h> volatile sig_atomic_t do_abort = 1; jmp_buf env; anular abort_handler (int i) {do_abort = 0; longjmp (env, 1);}
Robert Gamble
int main (void) {setjmp (env); pone ("En setjmp"); if (do_abort) {señal (SIGABRT, abort_handler); pone ("Llamando abortar"); abortar(); } pone ("¡No abortó!"); devuelve 0; }
Robert Gamble
En Ubuntu 7.04 esto imprime: En setjmp Llamando abortar En setjmp ¡No abortó!
Robert Gamble
5

Desde la página de manual de exit ():

La función exit () provoca la finalización normal del proceso y el valor del estado & 0377 se devuelve al padre.

Desde la página del manual abort ():

Abort () primero desbloquea la señal SIGABRT, y luego eleva esa señal para el proceso de llamada. Esto da como resultado la finalización anormal del proceso a menos que se capture la señal SIGABRT y el controlador de señal no regrese.

Federico A. Ramponi
fuente
4

abortenvía la SIGABRTseñal abortno vuelve a la persona que llama. El controlador predeterminado para la SIGABRTseñal cierra la aplicación. stdiolas secuencias de archivos se vacían y luego se cierran. Sin embargo, los destructores para instancias de clase C ++ no lo son (no estoy seguro en este caso, ¿quizás los resultados no están definidos?).

exittiene sus propias devoluciones de llamada, configuradas con atexit. Si se especifican devoluciones de llamada (o solo una), se invocan en el orden inverso al de su orden de registro (como una pila), entonces el programa se cierra. Al igual que con abort, exitno vuelve a la persona que llama. stdiolas secuencias de archivos se vacían y luego se cierran. Además, se llaman destructores para instancias de clase C ++.

extraño
fuente
exit puede tener múltiples funciones de devolución de llamada registradas a través de atexit, cuando se llama a la salida, todas las funciones de devolución de llamada se llamarán en el orden inverso en el que se registraron.
Robert Gamble
@Gamble, por supuesto, lo mencioné hace unos minutos en un comentario a la respuesta de @ Bondy. Editaré mi propia respuesta para reflejar eso.
extraño