Чтение онлайн

на главную - закладки

Жанры

Linux программирование в примерах

Роббинс Арнольд

Шрифт:

void callback1(void) { printf("callback1 called\n"); }

void callback2(void) { printf("callback2 called\n"); }

void callback3(void) { printf("callback3 called\n"); }

/* main --- регистрация функций и завершение */

int main(int argc, char **argv) {

 printf("registering callback1\n"); atexit(callback1);

 printf("registering callback2\n"); atexit(callback2);

 printf("registering callback3\n"); atexit(callback3);

 printf("exiting now\n");

 exit(0);

}

Вот что происходит при запуске:

$ ch09-atexit

registering callback1 /* Запуск главной программы */

registering callback2

registering callback3

exiting now

callback3 called /* Функции обратного вызова запускаются в обратном

порядке */

callback2 called

callback1 called

Как показывает пример, функции, зарегистрированные с помощью

atexit
, запускаются в порядке, обратном порядку их регистрации: последние первыми. (Это обозначается также LIFO — last-in-first-out — вошедший последним выходит первым).

POSIX определяет функцию

_exit
. В отличие от
exit
, которая вызывает функции обратного вызова и выполняет
<stdio.h>
– очистку,
_exit
является «сразу заканчивающейся» функцией:

#include <unistd.h> /* POSIX */

void _exit(int status);

Системе передается

status
, как и для
exit
, но процесс завершается немедленно. Ядро все еще делает обычную очистку: все открытые файлы закрываются, использованная адресным пространством память освобождается, любые другие ресурсы, использованные процессом, также освобождаются.

На практике функция

_Exit
ISO С идентична
_exit
. Стандарт С говорит, что от реализации функции зависит, вызывает ли
_Exit
зарегистрированные
atexit
функции и закрывает ли открытые файлы. Для систем GLIBC это не так, и функция ведет себя подобно
_exit
.

Время использовать

_exit
наступает, когда
exec
в порожденном процессе завершается неудачей. В этом случае вам не нужно использовать обычный
exit
, поскольку это сбрасывает на диск данные буферов, хранящиеся в потоках
FILE*
. Когда позже родительский процесс сбрасывает на диск свои копии буферов, данные буфера оказываются записанными дважды; это очевидно нехорошо.

Например, предположим, что вы хотите запустить команду оболочки и хотите сами выполнить

fork
и
exec
. Такой код выглядел бы следующим образом:

char *shellcommand = "...";

pid_t child;

if ((child = fork) == 0) { /* порожденный процесс */

 execl("/bin/sh", "sh", "-c", shellcommand, NULL);

 _exit(errno == ENOENT ? 127 : 126);

}

/* родитель продолжает */

Проверка значения

errno
и завершающего значения следуют соглашениям, используемым оболочкой POSIX. Если запрошенная программа не существует (
ENOENT
 — нет для неё элемента в каталоге), завершающее значение равно 127. В противном случае, файл существует, но
exec
не могла быть выполнена по какой-то другой причине, поэтому статус завершения равен 126. Хорошая мысль следовать этим соглашениям также и в ваших программах. Вкратце, чтобы хорошо использовать
exit
и
atexit
, следует делать следующее:

• Определить небольшой набор значений статуса завершения, которые ваша программа будет использовать для сообщения этой информации вызывающему. Используйте для них в своем коде константы

#define
или
enum
.

• Решить, имеет ли смысл наличие функций обратного вызова для использования с

atexit
. Если имеет, зарегистрировать их в
main
в соответствующий момент; например, после анализа опций и инициализации всех структур данных, которые функция обратного вызова должна очищать. Помните, что функции должны вызываться в порядке LIFO (последняя вызывается первой).

• Использовать

exit
для выхода из программы во всех местах, когда что-то идет не так и когда выход является правильным действием. Используйте коды ошибок, которые определили.

• Исключением является

main
, для которой можно использовать при желании
return
. Наш собственный стиль заключается обычно в использовании
exit
при наличии проблем и '
return 0
' в конце
main
, если все прошло хорошо.

• Использовать

_exit
или
_Exit
в порожденном процессе, если exec завершается неудачей.

9.1.6. Использование статуса завершения порожденного процесса

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

Родительский процесс, будь то первоначальный родитель или

init
, может получить статус завершения порожденного процесса. Или, посредством использования функций BDS, которые не стандартизованы POSIX, можно получить статус завершения вместе со сведениями об использовании ресурсов. Использование статуса осуществляется ожиданием окончания процесса: это известно также как пожинание (reaping) процесса [91] .

91

Мы это не придумываем. Терминология, конечно, не совсем правильна, но таким было чувство юмора разработчиков оригинальной Unix — Примеч. автора.

Между механизмами, которые ожидают завершения потомков, и сигнальными механизмами, которые мы еще не обсуждали, есть значительное взаимодействие. Что из них описать вначале представляет собой нечто вроде проблемы курицы и яйца; мы решили сначала поговорить сначала о механизмах ожидания порожденного процесса, а глава 10 «Сигналы» дает полный рассказ о сигналах.

Пока достаточно понять, что сигнал является способом уведомления процесса о том, что произошло некоторое событие. Процессы могут генерировать сигналы, которые посылаются самим себе, или сигналы могут посылаться извне другими процессами или пользователем за терминалом. Например, CTRL-C посылает сигнал «прерывания», a CTRL-Z посылает сигнал управления работой «стоп».

Поделиться:
Популярные книги

Хранилище

Старухин Евгений
5. Лесовик
Фантастика:
фэнтези
рпг
7.43
рейтинг книги
Хранилище

Кодекс Охотника. Книга XXIX

Винокуров Юрий
29. Кодекс Охотника
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга XXIX

Эволюционер из трущоб. Том 9

Панарин Антон
9. Эволюционер из трущоб
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Эволюционер из трущоб. Том 9

Громовая поступь. Трилогия

Мазуров Дмитрий
Громовая поступь
Фантастика:
фэнтези
рпг
4.50
рейтинг книги
Громовая поступь. Трилогия

Эпоха Опустошителя. Том II

Павлов Вел
2. Вечное Ристалище
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Эпоха Опустошителя. Том II

Беглец

Бубела Олег Николаевич
1. Совсем не герой
Фантастика:
фэнтези
попаданцы
8.94
рейтинг книги
Беглец

Государь

Кулаков Алексей Иванович
3. Рюрикова кровь
Фантастика:
мистика
альтернативная история
историческое фэнтези
6.25
рейтинг книги
Государь

По осколкам твоего сердца

Джейн Анна
2. Хулиган и новенькая
Любовные романы:
современные любовные романы
5.56
рейтинг книги
По осколкам твоего сердца

Чехов. Книга 2

Гоблин (MeXXanik)
2. Адвокат Чехов
Фантастика:
фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Чехов. Книга 2

Аспирант

Поселягин Владимир Геннадьевич
3. Рунный маг
Фантастика:
боевая фантастика
4.50
рейтинг книги
Аспирант

Путь к бессмертию 2

Покинтелица Евгений
2. Девятихвостый Богатырь
Фантастика:
попаданцы
сказочная фантастика
фэнтези
5.00
рейтинг книги
Путь к бессмертию 2

Дважды одаренный. Том VI

Тарс Элиан
6. Дважды одаренный
Фантастика:
аниме
альтернативная история
фэнтези
фантастика: прочее
5.00
рейтинг книги
Дважды одаренный. Том VI

Развод в 45. От любви до ненависти

Гофман Крис
6. Развод
Любовные романы:
остросюжетные любовные романы
5.40
рейтинг книги
Развод в 45. От любви до ненависти

Отряд

Валериев Игорь
5. Ермак
Фантастика:
альтернативная история
5.25
рейтинг книги
Отряд