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

на главную

Жанры

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 посылает сигнал управления работой «стоп».

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

Последний Паладин

Саваровский Роман
1. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин

Идущий в тени 8

Амврелий Марк
8. Идущий в тени
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Идущий в тени 8

Все еще не Герой!. Том 2

Довыдовский Кирилл Сергеевич
2. Путешествие Героя
Фантастика:
боевая фантастика
юмористическое фэнтези
городское фэнтези
рпг
5.00
рейтинг книги
Все еще не Герой!. Том 2

Совпадений нет

Безрукова Елена
Любовные романы:
любовно-фантастические романы
5.50
рейтинг книги
Совпадений нет

Муж на сдачу

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Муж на сдачу

На границе империй. Том 7. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
5.00
рейтинг книги
На границе империй. Том 7. Часть 4

Довлатов. Сонный лекарь

Голд Джон
1. Не вывожу
Фантастика:
альтернативная история
аниме
5.00
рейтинг книги
Довлатов. Сонный лекарь

СД. Восемнадцатый том. Часть 1

Клеванский Кирилл Сергеевич
31. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
6.93
рейтинг книги
СД. Восемнадцатый том. Часть 1

Темный Охотник

Розальев Андрей
1. КО: Темный охотник
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Охотник

Назад в СССР: 1985 Книга 3

Гаусс Максим
3. Спасти ЧАЭС
Фантастика:
попаданцы
альтернативная история
5.50
рейтинг книги
Назад в СССР: 1985 Книга 3

Целитель. Книга вторая

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

Сумеречный стрелок 8

Карелин Сергей Витальевич
8. Сумеречный стрелок
Фантастика:
городское фэнтези
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Сумеречный стрелок 8

Огненный князь 3

Машуков Тимур
3. Багряный восход
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Огненный князь 3

Магия чистых душ

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.40
рейтинг книги
Магия чистых душ