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

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

Жанры

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

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

Самурай. Трилогия

Оловянная Ирина
Фантастика:
боевая фантастика
8.74
рейтинг книги
Самурай. Трилогия

"Фантастика 2024-12". Компиляция. Книги 1-21

Шалашов Евгений Васильевич
Фантастика 2024. Компиляция
Фантастика:
фэнтези
боевая фантастика
детективная фантастика
альтернативная история
5.00
рейтинг книги
Фантастика 2024-12. Компиляция. Книги 1-21

Все будет по-моему! Часть 14

Wing-Span
14. Всё будет по-моему!
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Все будет по-моему! Часть 14

На грани

Вернер Ник
2. Дремир
Фантастика:
технофэнтези
фэнтези
5.00
рейтинг книги
На грани

Непорочность

Фэй Кира
Фантастика:
ужасы и мистика
9.16
рейтинг книги
Непорочность

Иммунитет Вселенной (Путь Знахаря)

Утолин Константин Владимирович
Фантастика:
научная фантастика
5.00
рейтинг книги
Иммунитет Вселенной (Путь Знахаря)

Время дня: ночь

Беатов Александр Георгиевич
Проза:
современная проза
5.00
рейтинг книги
Время дня: ночь

Антология советского детектива-44. Компиляция. Книги 1-20

Михеев Михаил Петрович
Детективы:
криминальные детективы
5.00
рейтинг книги
Антология советского детектива-44. Компиляция. Книги 1-20

В объятиях прошлого

Фуллер Ирина
1. Книжники
Фантастика:
детективная фантастика
5.00
рейтинг книги
В объятиях прошлого

Жрец Хаоса. Книга II

Борзых М.
2. Зов пустоты
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Жрец Хаоса. Книга II

Я был адъютантом Гитлера

фон Белов Николаус
Документальная литература:
биографии и мемуары
6.25
рейтинг книги
Я был адъютантом Гитлера

Жена со скидкой, или Случайный брак

Ардова Алиса
Любовные романы:
любовно-фантастические романы
8.15
рейтинг книги
Жена со скидкой, или Случайный брак

Бояръ-Аниме. Газлайтер. Том 32

Володин Григорий Григорьевич
32. История Телепата
Фантастика:
альтернативная история
аниме
фэнтези
попаданцы
6.40
рейтинг книги
Бояръ-Аниме. Газлайтер. Том 32

Последний реанорец. Том IX

Павлов Вел
8. Высшая Речь
Фантастика:
фэнтези
попаданцы
аниме
5.80
рейтинг книги
Последний реанорец. Том IX