Jaka jest różnica między exit () i abort ()?

133

Jaka jest różnica między exit()i w językach C i C ++ abort()? Próbuję zakończyć program po błędzie (nie jest to wyjątek).

Martin Liversage
źródło

Odpowiedzi:

118

abort()zamyka program bez wywoływania funkcji zarejestrowanych jako atexit()pierwsze i bez uprzedniego wywoływania destruktorów obiektów. exit()robi obie te rzeczy przed wyjściem z programu. Nie wywołuje jednak destruktorów dla obiektów automatycznych. Więc

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

Zniszczy ai bpoprawnie, ale nie wywoła destruktorów c. abort()nie nazwałby destruktorów żadnego z obiektów. Ponieważ jest to niefortunne, w standardzie C ++ opisano alternatywny mechanizm zapewniający prawidłowe zakończenie:

Obiekty z automatycznym czasem trwania są niszczone w programie, którego funkcja main()nie zawiera automatycznych obiektów i wykonuje wywołanie exit(). Kontrolę można przekazać bezpośrednio do takiego main(), rzucając wyjątek, który jest przechwytywany 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);
    }
}

Zamiast dzwonić exit(), zaaranżuj ten kod throw exit_exception(exit_code);.

Johannes Schaub - litb
źródło
2
+1, ponieważ chociaż Brian R. Bondy był dobry, podniosłeś problem abort / exit (nie wywołał destruktora obiektów stosu) i zaoferowałeś alternatywę dla procesu C ++ intensywnie korzystającego z RAII.
paercebal
Szukałem sposobu, aby zamknąć program bez dzwonienia do dtora, a Twoja odpowiedź jest właśnie tym, czego szukałem! Dzięki
acemtp
To jest całkowicie poprawne, oczywiście, jeśli faktycznie ma znaczenie, że twoje automatyczne niszczyciele obiektów nie są wywoływane :-)
Chris Huang-Leaver
O ile mi wiadomo, kolejną różnicą między zakończeniem a przerwaniem byłoby to, że przerwanie mogłoby (w zależności od konfiguracji systemu operacyjnego) doprowadzić do wygenerowania zrzutu pamięci.
Dirk Herrmann
33

abort wysyła sygnał SIGABRT, exit zamyka tylko aplikację wykonującą normalne czyszczenie.

Możesz obsłużyć sygnał przerwania , jak chcesz, ale domyślnym zachowaniem jest zamknięcie aplikacji również z kodem błędu.

abort nie spowoduje zniszczenia obiektów statycznych i globalnych członków, ale zakończy działanie .

Oczywiście, gdy aplikacja zostanie całkowicie zamknięta, system operacyjny zwolni niewykorzystaną pamięć i inne zasoby.

Zarówno w przypadku przerwania, jak i zakończenia programu wyjściowego (zakładając, że nie nadpisałeś domyślnego zachowania), kod powrotu zostanie zwrócony do procesu nadrzędnego, który uruchomił Twoją aplikację.

Zobacz poniższy przykład:

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;
}

Uwagi:

  • Jeśli abort nie jest komentowany: nic nie jest drukowane, a destruktor jakiegoś obiektu nie zostanie wywołany.

  • Jeśli abort jest komentowane jak powyżej: zostanie wywołany jakiś destruktor obiektów, otrzymasz następujący wynik:

wyjście z funkcji 2
wyjście z funkcji 1

Brian R. Bondy
źródło
Tutaj nazywa się exit function 2 TO wyjście z funkcji 1. gcc 4, Linux 2.6.
strager
1
Strona podręcznika dla atexit mówi: "Funkcje [zarejestrowane przy użyciu atexit] są wywoływane w odwrotnej kolejności; żadne argumenty nie są przekazywane."
strager
@strager ma rację, funkcje zarejestrowane przez atexit powinny być wywoływane w odwrotnej kolejności, gdy wywoływane jest wyjście lub główny powrót.
Robert Gamble,
Uruchomiono test i wygląda na to, że destruktory instancji globalnych są wywoływane w końcu wywołania zwrotne atexit.
strager
+1 za przypomnienie ludziom, że system operacyjny ostatecznie zwolni wszystkie przydzielone zasoby nawet po wywołaniu abort ().
Fingolfin
10

Następujące rzeczy mają miejsce, gdy program wywołuje exit():

  • Funkcje zarejestrowane przez atexit funkcję
  • Wszystkie otwarte strumienie są opróżniane i zamykane, a pliki utworzone przez tmpfile są usuwane
  • Program kończy działanie podanym kodem zakończenia do hosta

Funkcja abort() wysyła SIGABRTsygnał do bieżącego procesu, jeśli nie zostanie przechwycony, program zostanie zakończony bez gwarancji, że otwarte strumienie zostaną opróżnione / zamknięte lub że pliki tymczasowe utworzone za pomocą tmpfilezostaną usunięte, atexitzarejestrowane funkcje nie zostaną wywołane, a zerowy kod wyjścia jest zwracany do hosta.

Robert Gamble
źródło
hmm. standard mówi, że program nie jest przerywany tylko wtedy, gdy program obsługi sygnału „nie zwraca”. masz się dobrze z C. czy możesz sobie wyobrazić scenariusz, który pozwoliłby na kontynuowanie normalnego wykonywania bez powrotu? Wyobrażam sobie longjmp, ale nie jestem pewien, jak zachowuje się w programach obsługi sygnału.
Johannes Schaub - litb
Ogólnie wywołanie longjmp z programu obsługi sygnału jest nieokreślone, ale istnieje specjalny przypadek, w którym sygnał został wygenerowany przez podniesienie / przerwanie, więc myślę, że byłoby to teoretycznie możliwe, chociaż nie sądzę, żebym kiedykolwiek to zrobił. Teraz będę musiał spróbować;)
Robert Gamble,
1
Wydaje się, że to działa (podzielone na wiele postów ze względu na limit 300 znaków): #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h> volatile sig_atomic_t do_abort = 1; jmp_buf env; void abort_handler (int i) {do_abort = 0; longjmp (env, 1);}
Robert Gamble
int main (void) {setjmp (env); puts ("W setjmp"); if (do_abort) {signal (SIGABRT, abort_handler); puts ("Calling abort"); anulować(); } puts ("Nie przerwano!"); return 0; }
Robert Gamble,
W systemie Ubuntu 7.04 wypisuje się: W setjmp Calling abort At setjmp Nie przerwano!
Robert Gamble,
5

Ze strony podręcznika exit ():

Funkcja exit () powoduje normalne zakończenie procesu, a wartość statusu & 0377 jest zwracana do rodzica.

Ze strony podręcznika abort ():

Abort () najpierw odblokowuje sygnał SIGABRT, a następnie podnosi ten sygnał dla procesu wywołującego. Powoduje to nieprawidłowe zakończenie procesu, chyba że sygnał SIGABRT zostanie przechwycony, a program obsługi sygnału nie powróci.

Federico A. Ramponi
źródło
4

abortwysyła SIGABRTsygnał. abortnie wraca do dzwoniącego. Domyślna obsługa SIGABRTsygnału zamyka aplikację. stdiostrumienie plików są opróżniane, a następnie zamykane. Destruktory dla instancji klas C ++ nie są jednak (nie jesteś pewien tego - być może wyniki są nieokreślone?).

exitma własne wywołania zwrotne, ustawiane za pomocą atexit. Jeśli podano wywołania zwrotne (lub tylko jeden), są one wywoływane w kolejności odwrotnej do ich kolejności rejestracji (jak stos), a następnie program kończy działanie. Podobnie jak w przypadku abort, exitnie wraca do dzwoniącego. stdiostrumienie plików są opróżniane, a następnie zamykane. Wywoływane są również destruktory dla instancji klas C ++.

strager
źródło
exit może mieć wiele funkcji zwrotnych zarejestrowanych przez atexit, kiedy wywoływane jest exit, wszystkie funkcje zwrotne będą wywoływane w odwrotnej kolejności, w jakiej zostały zarejestrowane.
Robert Gamble,
@Gamble, oczywiście, wspomniałem o tym kilka minut temu w komentarzu do odpowiedzi @ Bondy. Zredaguję własną odpowiedź, aby to odzwierciedlić.
strager