Подтвердить что ты не робот

Как будет _Exit вести себя в С++-программе?

C99 предлагает функцию _Exit, которая выходит "немедленно", хотя делает может закрыть дескрипторы файлов. Unix/POSIX расширяет это поведение, поручая закрытие всех fd без очистки (и предлагает синоним _Exit).

Будут ли эти функции вызывать деструкторы для объектов static при вызове из программы на С++? Предоставляет ли стандарт С++ какие-либо гарантии относительно _Exit?

(Вдохновленный этот вопрос, я вдруг подумал, что происходит в типичной fork - exec - _Exit идиоме в С++.)

4b9b3361

Ответ 1

Он просто не существует в стандартном С++, поэтому никаких гарантий нет.

Планируется включить в С++ 0x. Это указывает (§18.5):

Функция _Exit (int status) имеет дополнительное поведение в этом Международный стандарт:

- Программа завершается без выполнение деструкторов для объектов автоматическое, потоковое или статическое хранилище продолжительность и без функций вызова передается atexit() (3.6.3).

Followup:

ISO утвержден С++ 0x от 12 августа 2011 года.

Ответ 2

Во-первых, никакая форма выхода программы не будет автоматически вызывать деструкторы для объектов кучи (подразумевается в ISO/IEC 14882: 1998 (E) 12.4.10).

Вызов exit() не будет вызывать деструкторы для объектов с автоматической продолжительностью, так как он не возвращается через свои охватывающие области (3.6.1.4). Однако деструкторы для статических объектов будут вызываться в обратном порядке построения (18.3.8).

Вызов abort() не вызывает никаких деструкторов для любого типа объекта и не вызывает функции atexit() зарегистрированных функций (18.3.3). Стандартная копия С++, которую я имею здесь, немного устарела и не упоминает непосредственно _exit или _exit, но я бы предположил, что если они присутствуют, они должны вести себя одинаково, то есть не вызывать никаких деструкторов. В частности, в стандарте C99 _Exit() пропускает обработчики atexit (это реализация определяет, сброшены ли потоковые буферы, открытые потоки закрыты или удалены временные файлы).

Обратите внимание, что abort() может быть отменено сигналом захвата SIGABRT (ISO/IEC 9899: 1999 (E) 7.20.4.1.2 - У меня здесь только C99, но я ожидаю, что он будет таким же в версии ссылка на С++). _Exit() не может.

В более практичной заметке, в большинстве реализаций unix abort() и _Exit(), abort() вызывает SIGABRT, а _Exit() просто вызывает вызов операционной системы, чтобы немедленно завершить процесс. Это означает, что основными отличиями являются:

  • Вы можете указать код выхода для _Exit()
  • abort() может быть захвачен обработчиком сигнала
  • В зависимости от конфигурации системы, OS и ulimits, abort() может привести к дампу ядра или тому подобному

В шаблоне fork()/exec() _Exit(), вероятно, будет предпочтительнее, чтобы избежать возможности дампа ядра.

Ответ 3

Технически, _Exit не определяется стандартом С++, поэтому вы даже не можете его вызывать из 100% -ной переносной программы на С++. Стандарт С++ 03 включает ссылку на стандарт C89 (aka C90 или ANSI C), тогда как _Exit определяется только в новом стандарте C99. Я не уверен, какая версия C для следующего стандарта С++ 0x включает, но я бы предположил, что он основан на C99.

В любом случае, вот соответствующие положения из соответствующих языковых стандартов:

_Exit не гарантируется закрытие дескрипторов файлов. Из C99 §7.20.4.4/2 (внимание мое):

Функция _Exit вызывает нормальное завершение программы, а управление возвращается в среду хоста. Вызывается функция, зарегистрированная функцией atexit или обработчиками сигналов, зарегистрированными функцией signal. Статус, возвращаемый в среду хоста, определяется так же, как и для функции exit (7.20.4.3). Открыты ли открытые потоки с неписаными буферизованными данными, открытые потоки закрыты или удалены временные файлы. Реализация определена.

Напомним, что реализация, определяемая реализацией, означает, что реализация (то есть компилятор компилятора и среда выполнения) может делать все возможное, но она должна документировать, что она делает.

Из С++ 03 §3.6.3/1:

Деструкторы (12.4) для инициализированных объектов времени статического хранения (объявлены в области блока или в области пространства имен) вызывается в результате возврата из main и в результате вызова exit (18.3). Эти объекты уничтожаются в обратном порядке завершения их конструктора или завершения их динамической инициализации. Если объект инициализирован статически, объект уничтожается в том же порядке, как если бы объект был динамически инициализирован. Для объекта массива или типа класса все подобъекты этого объекта уничтожаются до уничтожения любого локального объекта со статической продолжительностью хранения, инициализированной во время построения подобъектов.

§3.6.3/4:

Вызов функции

    void abort();

объявленный в <cstdlib>, завершает работу программы без выполнения деструкторов для объектов с автоматическим или статическим хранением и без вызова функций, переданных в atexit().

Практически в большинстве реализаций деструкторы глобальных объектов реализуются через atexit, так что вы увидите, что _Exit не будет вызовите деструкторы для глобальных объектов, хотя это поведение не гарантируется (поскольку _Exit и С++ не гарантированы, что они существуют на одном языке).

Ответ 4

Обратите внимание, что хотя С++ не указывает _Exit, а C99 оставляет его определяемым реализацией, очищает ли он буферы, POSIX требует, чтобы он не очищал буферы (поскольку это нарушило бы основное использование _Exit/_Exit, т.е. сбой обработки execve после fork). Поскольку POSIX не согласуется со стандартами С++ или не откладывает на них ни на что, я думаю, что очень маловероятно, что будущая версия стандарта С++ попытается изменить любое из этого. Вероятно, он либо оставит _Exit неуказанным, либо укажет, что он определен в реализации.

Ответ 5

С++ 0x определяет новую функцию std:: quick_exit, которая завершает процесс без вызова каких-либо деструкторов. Просто проверено, g++ - 4.4.5 уже предоставляет его.

Ответ 6

Существует интересный анализ здесь в связи с concurrency и уничтожением объекта. Насколько я знаю, деструкторы не будут называться. В текущем стандарте нет ничего об этом.

Ответ 7

Вызов статических деструкторов определяется в терминах atexit. _exit (или _Exit) определяется не для запуска обработчиков atexit. Поэтому статические деструкторы не должны вызываться какой-либо реализацией.

Автоматические деструкторы даже не вызываются при вызове exit().

Таким образом, любое разумное определение семантики _Exit для С++ не будет запускать деструкторы.

Ответ 8

Я быстро проверил gcc на Mac OS, и мои деструкторы не вызывались.

struct A
{
    ~A()
    {
        puts("A::~A");
    }
};

A globalA;

int main()
{
    A localA;
    _exit(0); // or _Exit(0)
    return 0;
}

exit(0), с другой стороны, вызывает globalA деструктор.

Ответ 9

fork(), exec() и _exit() все определены POSIX, и они на протяжении многих лет предписывают C99 _exit(). Программы, которые используют fork/exec/_exit, не переносятся ни в каждую систему, которая поддерживает С++.

В отношении _exit() в частности, это вызов операционной системы, который (под POSIX) закрывает файлы и завершает процесс напрямую (но не обязательно быстро). Это обходит любые механизмы С++ для вызова деструкторов.

Даже с _exit() или аналогичным, предоставляемым С++ 0x, я сомневаюсь, что есть много причин использовать это в сочетании с fork. Вероятно, это просто обеспечивает более широкую переносимость для "быстрого выхода" в других контекстах. Эта функция уже распространяется на _exit(), если вы используете POSIX API.

Окончание программы адресовано в разделе С++ 2003 [3.6.3]. В нем говорится, что статические объекты разрушаются неявно, когда main() возвращается и когда вызывается exit(). В нем также говорится, что такие объекты НЕ разрушаются при вызове abort(). _exit() не рассматривается в стандарте С++ 2003, но тот факт, что он предназначен для обхода очистки по языку, описан в документации POSIX. Этот эффект далее подтверждается тем, что указано, и тем, что НЕ указано в стандарте С++.