Медленные системные вызовы требуют неопределенного времени для своего завершения. Системные вызовы, которые для завершения своей работы ожидают непредсказуемых ресурсов, таких как другие процессы, сетевые данные либо действия со стороны человека, рассматриваются как медленные. Семейство системных вызовов wait()
, например, не возвращают управление до тех пор, пока дочерние процессы не завершатся. Поскольку невозможно узнать, насколько долго продлится это ожидание, считается, что wait()
— медленный системный вызов. Системные вызовы доступа к файлам рассматриваются как медленные, если они обращаются к медленным файлам, и быстрые — если к быстрым файлам[58].
Обязанностью процесса является обработка EINTR
и перезапуск системных вызовов в случае необходимости. Хотя это обеспечивает всю функциональность, которая требуется людям, намного сложнее написать код, который обрабатывает сигналы. Всякий раз когда read()
вызывается на медленном файловом дескрипторе, код должен проверять его возврат на равенство EINTR
, и перезапускать вызов, либо он не будет делать то, что ожидается.
Чтобы "упростить" ситуацию, 4.2BSD автоматически перезапускает такие системные вызовы (особенно read()
и write()
). Поэтому для большинства операций программы более не должны беспокоиться об EINTR
, поскольку выполнение системных вызовов продолжится после того, как процесс обработает сигнал. В последних версиях Unix изменен перечень системных вызовов, которые автоматически перезапускаются, a 4.3BSD позволяет вам выбрать, какие системные вызовы перезапускать. Стандарт обработки сигналов POSIX не указывает, какое поведение должно применяться, но все популярные системы согласны в том, как обрабатывать этот случай. По умолчанию системные вызовы не перезапускаются, но для каждого сигнала процесс может установить флаг, который указывает, что система должна перезапускать системные вызовы, прерванные этим сигналом.
12.2. Программный интерфейс сигналов Linux и POSIX
12.2.1. Посылка сигналов
Посылка сигналов от одного процесса другому обычно осуществляется с помощью системного вызова kill()
. Этот системный вызов подробно обсуждался в главе 10. Вариантом kill()
является tkill()
, который не предназначен для прямого использования в программах.
int tkill(pid_t pid, int signum);
Существуют два отличия между kill()
и tkill()
[59]. Первое: pid
должен быть положительным числом; tkill()
не может использоваться для отправки сигналов группам процессов, как это может kill()
. Другое отличие позволяет обработчикам сигналов определять, применялся ли вызов kill()
или tkill()
для генерации сигнала: подробности см. далее в главе.
Функция raise()
, которая представляет собой способ генерации сигналов, указанный ANSI/ISO, использует системный вызов tkill
() для генерации сигналов в системах Linux.
int raise(int signum);
Функция raise()
посылает текущему процессу сигнал, указанный в signum
[60].
12.2.2. Использование sigset_t
Большинство функций сигналов POSIX принимают набор сигналов в качестве одного из своих параметров (или части одного из параметров). Тип данных sigset_t
служит для представления набора сигналов и определен в
. POSIX определяет пять функций для манипулирования наборами сигналов.
#include
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
int sigemptyset(sigset_t *set); | Делает пустым набор сигналов, на который указывает set (никаких сигналов в set представлено не будет). |
int sigfillset(sigset_t *set); | Включает все доступные сигналы в set . |
int sigaddset(sigset_t *set, int signum); | Добавляет сигнал signum в набор set . |
int sigdelset(sigset_t *set, int signum); | Удаляет сигнал signum из набора set . |
int sigismember(const sigset_t *set, int signum); | Возвращает не 0, если сигнал signum содержится в set . В противном случае возвращает 0. |