
- •Часть 3. Подсистема управления процессами
- •Глава 7. Среда окружения процесса 2
- •Глава 8. Управление процессами 16
- •Глава 9. Взаимоотношения между процессами 49
- •Глава 10. Сигналы 62
- •Глава 11. Процессы-демоны 98
- •Глава 7. Среда окружения процесса
- •7.1. Введение
- •7.2. Функция main
- •7.3. Завершение работы процесса
- •7.3.1. Функции семейства exit
- •7.3.2. Функция atexit
- •7.4. Аргументы командной строки
- •7.5. Раскладка памяти программы на языке c
- •7.6. Разделяемые библиотеки
- •7.7. Распределение памяти
- •7.8. Список переменных окружения
- •7.9. Функции getrlimit и setrlimit
- •7.10. Выводы по главе 7
- •7.11. Упражнения по главе 7
- •Глава 8. Управление процессами
- •8.1. Введение
- •8.2. Идентификаторы процесса
- •8.3. Функция fork
- •8.4. Совместное использование файлов
- •8.5. Функция exit
- •8.6. Функции wait и waitpid
- •8.7. Гонка за ресурсами
- •8.8. Функция exec
- •8.9. Изменение идентификаторов пользователя и группы
- •8.9.1. Функции setreuid и setregid
- •8.9.2. Функции seteuid и setegid
- •8.9.3. Идентификаторы группы
- •8.10. Интерпретируемые файлы
- •8.11. Функция system
- •8.12. Идентификация пользователя
- •8.13. Временные характеристики процесса
- •8.14. Выводы по главе 8
- •8.15. Упражнения по главе 8
- •Глава 9. Взаимоотношения между процессами
- •9.1. Введение
- •9.2. Вход с терминала
- •9.2.1. Вход в систему с терминала в bsd-системах
- •9.2.2. Вход в систему с терминала в Linux
- •9.3. Вход в систему через сетевое соединение
- •9.3.1. Вход в систему через сетевое соединение в bsd-системах
- •9.3.2. Вход в систему через сетевое соединение в Linux
- •9.4. Группы процессов
- •9.5. Сессии
- •9.6. Управляющий терминал
- •9.7. Функции tcgetpgrp, tcsetpgrp и tcgetsid
- •9.8. Выводы по главе 9
- •9.9. Упражнения по главе 9
- •Глава 10. Сигналы
- •10.1. Введение
- •10.2. Концепция сигналов
- •10.3. Функция signal
- •10.3.1. Запуск программы
- •10.3.2. Создание процесса
- •10.4. Ненадежные сигналы
- •10.5. Прерванные системные вызовы
- •10.6. Реентерабельные функции
- •10.7. Надежные сигналы. Терминология и семантика
- •10.8. Функции kill и raise
- •10.9. Функции alarm и pause
- •10.10. Наборы сигналов
- •10.11. Функция sigprocmask
- •10.12. Функция sigpending
- •10.13. Функция sigaction
- •10.14. Функция sigsuspend
- •10.15. Функция abort
- •10.16. Функция system
- •10.17. Функция sleep
- •10.18. Дополнительные возможности
- •10.19. Выводы по главе 10
- •10.20. Упражнения по главе 10
- •Глава 11. Процессы-демоны
- •11.1. Введение
- •11.2. Характеристики демонов
- •11.3. Правила программирования демонов
- •11.4. Журналирование ошибок
- •11.5. Демоны в единственном экземпляре
- •11.6. Соглашения для демонов
- •11.7. Модель клиент-сервер
- •11.8. Выводы по главе 11
- •11.9. Упражнения по главе 11
10.15. Функция abort
Ранее уже упоминалось, что вызов функции abort приводит к аварийному завершению процесса.
#include <stdlib.h>
void abort (void);
/* эта функция никогда не возвращает управление */
Эта функция передает сигнал SIGABRT вызывающему процессу. (Процессы не должны игнорировать этот сигнал). Стандарт ISO C определяет, что функция abort должна извещать операционную систему об аварийном завершении с помощью вызова функции raise(SIGABRT).
Стандарт ISO C требует, чтобы функция abort никогда не возвращала управление, даже в том случае, когда приложение перехватывает сигнал SIGABRT. Если сигнал перехватывается, то единственный способ для обработчика не вернуть управление в вызывающий процесс – это вызвать одну из функций exit, _exit или _Exit. Помимо этого, стандарт Posix.1 указывает, что функция abort должна выполняться даже в том случае, если процесс заблокировал или игнорирует сигнал SIGABRT.
Процессу позволено перехватывать сигнал SIGABRT, чтобы он мог выполнить необходимые действия перед завершением. Если процесс не завершается из обработчика сигнала, то стандарт Posix.1 указывает, что функция abort должна завершить процесс, когда обработчик сигнала вернет управление.
Стандарт ISO C оставляет на усмотрение реализации решение вопроса о сбросе буферов ввода-вывода и удалении временных файлов (раздел 5.10). Стандарт Posix.1 пошел гораздо дальше и требует, чтобы функция abort, если она завершает процесс, воздействовала на открытые потоки ввода-вывода так же, как функция fclose.
Пример. В листинге 10.18 приводится реализация функции abort, соответствующая требованиям стандарта Posix.1.
Листинг 10.18. Реализация функции abort, соответствующая требованиям стандарта Posix.1
#include <signal.h>
#include <stdio.h>
void my_abort(void) /* функция abort() в стиле POSIX */
{
sigset_t mask;
struct sigaction action;
/*
* Вызывающий процесс не может игнорировать SIGABRT. Если это так,
* то необходимо сбросить диспозицию сигнала в значение по умолчанию.
*/
sigaction(SIGABRT, NULL, &action);
if (action.sa_handler == SIG_IGN)
{
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
}
if (action.sa_handler == SIG_DFL)
fflush(NULL); /* сбросить буферы всех потоков ввода-вывода */
/*
* Вызывающий процесс не может заблокировать SIGABRT;
* убедитесь, что он не заблокирован.
*/
sigfillset(&mask);
sigdelset(&mask, SIGABRT); /* в маске разблокирован только SIGABRT */
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT); /* послать сигнал */
/*
* Если мы вернулись сюда, значит, процесс перехватил SIGABRT
* и вернул управление.
*/
fflush(NULL); /* сбросить буферы всех потоков ввода-вывода */
action.sa_handler = SIG_DFL; /* Установить диспозицию сигнала */
sigaction(SIGABRT, &action, NULL); /* в значение по умолчанию. */
sigprocmask(SIG_SETMASK, &mask, NULL); /* на всякий случай ... */
kill(getpid(), SIGABRT); /* и еще раз */
exit(1); /* этот вызов никогда не будет выполнен ... */
}
Прежде всего, мы проверяем, будет ли выполнено действие по умолчанию для сигнала SIGABRT – если это так, то мы сбрасываем буферы всех открытых потоков ввода-вывода. Это не равносильно вызову функции fclose (поскольку мы лишь сбрасываем буферы, а не закрываем потоки), но система сама закроет все открытые файлы, когда процесс завершится. Если процесс перехватил сигнал и вернул управление, мы опять сбрасываем буферы всех открытых потоков ввода-вывода, поскольку процесс мог выводить некоторые данные в обработчике сигнала. Единственное, что мы не сможем обработать, –это вызов функции _exit или _Exit из обработчика. В этом случае все данные, оставшиеся в буферах ввода-вывода, будут утеряны. Но мы будем исходить из предположения, что вызывающий процесс просто не пожелал сбрасывать содержимое буферов.
В разделе 10.8 мы говорили, что если в результате вызова функции kill будет сгенерирован сигнал для вызывающего процесса и этот сигнал не заблокирован, (что гарантирует функция my_abort из листинга 10.18), то этот сигнал (или любой другой незаблокированный сигнал, ожидающий обработки) будет доставлен процессу еще до выхода из функции kill. В данном случае мы блокируем доставку всех сигналов, за исключением SIGABRT, поэтому мы наверняка знаем, что если вызов функции kill вернул управление, значит, сигнал был перехвачен и обработан процессом.