Как _Exit будет вести себя в программе на C ++? - PullRequest
12 голосов
/ 22 января 2011

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

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

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

Ответы [ 9 ]

11 голосов
/ 22 января 2011

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

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

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

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

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

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

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

10 голосов
/ 22 января 2011

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

Это - это , запланированное для включения в C ++ 0x.Это указывает (§18.5):

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

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

Продолжение:

C ++ 0x, одобренный ISO 12 августа 2011 г.

3 голосов
/ 22 января 2011

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

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

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

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

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

Из C ++ 03 §3.6.3 / 1:

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

§3.6.3 / 4

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

void abort();

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

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

2 голосов
/ 22 января 2011

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

2 голосов
/ 22 января 2011

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

1 голос
/ 22 января 2011

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

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

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

1 голос
/ 22 января 2011

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

0 голосов
/ 17 февраля 2011

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

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

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

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

0 голосов
/ 22 января 2011

Я провел быстрый тест с 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деструктор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...