Яка різниця між виходом () і перервати ()?


Відповіді:


116

abort()виходить із програми без виклику функцій, зареєстрованих atexit()спочатку, та без виклику спочатку деструкторів об’єктів. exit()робить обидва перед виходом із програми. Однак він не викликає деструктори для автоматичних об'єктів. Так

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

Зруйнує aі bналежним чином, але не закличе деструкторів c. abort()не викликав би руйнівників жодного об'єкта. Оскільки це прикро, Стандарт C ++ описує альтернативний механізм, який забезпечує належне припинення:

Об'єкти з автоматичною тривалістю зберігання знищуються в програмі, функція якої main()не містить автоматичних об'єктів і виконує виклик до exit(). Контроль можна передати безпосередньо такому main(), кинувши виняток, який потрапив у 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);
    }
}

Замість дзвінків exit()організуйте цей код throw exit_exception(exit_code);замість цього.


2
+1 тому, що, хоча Брайан Р. Бонді був хорошим, ви все-таки підняли проблему переривання або виходу (не деструктор зв'язаних об'єктів стека) і запропонували альтернативу для RAII-інтенсивного C ++ процесу.
— paercebal

Я шукав спосіб виходу з програми без виклику dtor, і ваша відповідь - це саме те, що я шукав! Дякую
— acemtp

Це абсолютно правильно, звичайно, якщо насправді має значення те, що ваші автоматичні деструктори не викликаються :-)
— Кріс Хуан-Лівер

Наскільки мені відомо, ще одна відмінність між виходом і перериванням полягала б у тому, що переривання може (залежно від конфігурації операційної системи) призвести до генерації дампів ядра.
— Дірк Геррманн

33

аборт надсилає сигнал SIGABRT, вихід просто закриває програму, виконуючи звичайну очистку.

Ви можете обробляти сигнал переривання, як хочете, але поведінка за замовчуванням - це закрити програму, а також кодом помилки.

аборт не буде виконувати знищення об'єктів ваших статичних і глобальних членів, але вихід буде.

Звичайно, якщо програма повністю закрита, операційна система звільнить незапалену пам'ять та інші ресурси.

В обох переривання і вихід завершення програми (якщо ви не перевизначити поведінку за замовчуванням), код повернення буде повернений в батьківський процес , який почав свій додаток.

Дивіться наступний приклад:

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

Коментарі:

  • Якщо аборт не коментується: нічого не друкується і деструктор якогось об'єкта не буде викликаний.

  • Якщо аборт буде прокоментовано як вище: буде викликано якийсь деструктор об'єкта, ви отримаєте такий вихід:

функція виходу 2
функція виходу 1


Тут вона називається функцією виходу 2, а потім функція виходу 1. gcc 4, Linux 2.6.
— страгер

1
Сторінка Man для atexit говорить: "Функції [зареєстровані за допомогою atexit] викликаються у зворотному порядку; аргументи не передаються."
— страгер

@strager має рацію, функції, зареєстровані atexit, повинні викликатися в зворотному порядку, коли викликається або вихід, або основна віддача.
— Роберт Гембл

Пройшов тест, і, схоже, деструктори глобальних екземплярів викликаються після всіх зворотних зворотних викликів.
— страгер

+1 для нагадування людям про те, що ОС врешті-решт звільнить усі виділені ресурси навіть після виклику аборту ().
— Фінгольфін

10

Коли програма викликає exit(), відбувається таке:

  • Функції, зареєстровані atexitфункцією, виконуються
  • Усі відкриті потоки змиваються та закриваються, файли, створені файлом tmpfile, видаляються
  • Програма закінчується заданим кодом виходу на хост

Функція abort() надсилає SIGABRTсигнал поточному процесу, якщо його не зафіксували, програма припиняється без гарантії, що відкриті потоки змиваються / закриваються або tmpfileвидаляються тимчасові файли через , atexitзареєстровані функції не викликаються, а не- нульовий статус виходу повертається хосту.


хм. стандарт говорить, що програма не припиняється, лише якщо обробник сигналу "не повертається". Ви добре розумієтесь із C. Чи можете ви уявити будь-який сценарій, який би дозволив продовжувати нормальне виконання без повернення? Я уявляю longjmp, але я не впевнений, як він поводиться в обробниках сигналів.
— Йоханнес Шауб - ліб

Взагалі, виклик longjmp від обробника сигналу не визначений, але є особливий випадок, коли сигнал генерувався з підвищенням / перериванням, тому я думаю, що це теоретично можливо, хоча я не думаю, що я ніколи не бачив цього. Тепер мені доведеться спробувати;)
— Роберт Гембл

1
Це, здається, працює (розбивається на кілька постів через обмеження 300 знаків): #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h> летючий sig_atomic_t do_abort = 1; jmp_buf env; void abort_handler (int i) {do_abort = 0; longjmp (env, 1);}
— Роберт Гембл

int main (void) {setjmp (env); put ("At setjmp"); if (do_abort) {сигнал (SIGABRT, abort_handler); ставить ("Виклик перервати"); аборт (); } put ("Не перервав!"); повернути 0; }
— Роберт Гембл

На Ubuntu 7.04 ця версія друкується: На setjmp Calling abort At setjmp Not abort!
— Роберт Гембл

5

З сторінки керівництва exit ():

Функція exit () викликає нормальне завершення процесу, і значення статусу & 0377 повертається батьківському.

З сторінки посібника про переривання ()

Аборт () спочатку розблокує сигнал SIGABRT, а потім піднімає цей сигнал для процесу виклику. Це призводить до ненормального припинення процесу, якщо сигнал SIGABRT не зачеплений і обробник сигналу не повернеться.


4

abortпосилає SIGABRTсигнал. abortне повертається до абонента. Обробник за замовчуванням для SIGABRTсигналу закриває програму. stdioпотоки файлів промиваються, потім закриваються. Однак деструктори для екземплярів класу C ++ не є (не впевнені в цьому - можливо, результати не визначені?).

exitмає свої зворотні дзвінки, встановлені з atexit. Якщо зворотні виклики вказані (або лише один), вони викликаються в порядку, зворотному їх порядку реєстрації (як стек), тоді програма виходить. Як і у випадку abort, exitне повертається до абонента. stdioпотоки файлів промиваються, потім закриваються. Також викликаються деструктори для екземплярів класу C ++.


Вихід може мати кілька функцій зворотного виклику, зареєстрованих через atexit, коли вихід викликається, всі функції зворотного виклику будуть викликатися у зворотному порядку, в якому вони були зареєстровані.
— Роберт Гембл

@ Gamble, Звичайно, я згадував про це кілька хвилин тому в коментарі до відповіді @ Bondy. Я відредагую власну відповідь, щоб це відобразити.
— страгер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.