Что произойдет, если выход произойдет внутри обработчика atexit? - PullRequest
1 голос
/ 02 июля 2019

Я знаю, что atexit используется для регистрации обработчика функции. Затем, когда происходит выход в коде, эта функция вызывается. Но что если выход происходит внутри обработчика функции?

Я ожидал бесконечный цикл, но на самом деле программа завершается нормально. Почему?

void handler(){
    printf("exit\n");
    exit(1);
}

int maint(int argc, char *argv[]) {
    atexit(handler);
    exit(1);
}

Ответы [ 3 ]

4 голосов
/ 02 июля 2019

Поведение не определено.

7.22.4.4 Функция выхода

2 Функция выхода вызывает нормальное завершение программы впроисходят.Функции, зарегистрированные функцией at_quick_exit, не вызываются.Если программа вызывает функцию выхода более одного раза или вызывает функцию quick_exit в дополнение к функции выхода, поведение не определено.

Вызов exit в обработчике at_exit (то естьзапуск во время обычной обработки exit), безусловно, является вторым вызовом для выхода.

Выход обычно является возможным поведением, но, видя, что что-то может произойти (характер поведения не определен), он вполне можетпривести к катастрофе.Лучше не делать этого.

0 голосов
/ 03 июля 2019

POSIX.1 говорит, что результат вызова exit (3) более одного раза (т. Е. Вызов exit (3) внутри функции, зарегистрированной с помощью atexit ()) , не определен . В некоторых системах (но не в Linux) это может привести к бесконечной рекурсии;Переносимые программы не должны вызывать exit (3) внутри функции, зарегистрированной с помощью atexit ().

найдена здесь

0 голосов
/ 03 июля 2019

Как уже указывалось, поведение не определено .... но, несмотря на это, пытаясь оправдать наблюдаемое вами поведение, разработчики библиотек обычно склонны справляться со странным поведением программиста (например, , по крайней мере, странно). вызова exit(), пока программа находится в exit()) Я скажу:

  • Возможно, что функция exit(3) перед вызовом любого из выходаобработчики, просто отменяет регистрацию из списка обработчиков сигналов.Это заставит функцию exit(2) вызывать каждый обработчик выхода только один раз и не вызывать рекурсивный обработчик.Просто попробуйте зарегистрировать его снова, чтобы увидеть, что произойдет, было бы хорошим упражнением.

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

  • Возможно, ваше ожидаемое поведение может привести к переполнению стака (здесь нет каламбура:))

  • Возможно ...

Что бы ни происходило, это часть UB, прокомментированная в других ответах, но для библиотеки, которая пытается расширить стандарт и вести себя нормально, вероятно, лучшее поведениечтобы избежать рекурсивных вызовов в обработчиках выхода некоторыми из предложенных способов.

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

Вы, вероятно, думаете о exit(3) как о функции, которую никогда не следует вызывать дважды (кромеот рекурсивно, как вы разоблачаете), но вам кажется, что у вас есть несколько потоков в вашей программе, и два из них одновременно вызывают функцию exit(3).

Вероятно, наилучшим поведением будет иметь какой-то семафор, который позволяетобработчики должны быть защищены от взаимного доступа ... но лучший способ получить список обработчиков для кратковременного взлома - это отсоединить один обработчик от списка (давайте рассмотрим список как очередь, куда каждый поток приходит и берет обработчик)они получают обработчик, разблокируют очередь и затем выполняют ее.Это может привести к тому, что каждый обработчик выполняется одним потоком из тех, которые вызвали exit()Первое, с чем сталкивается разработчик, это как работать с несколькими потоками, вызывающими exit() одновременно.

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