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


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);.


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


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.


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.


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 ++.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.