10.6.2. Наборы сигналов: sigset_t и связанные функции
Маска сигналов процесса является списком сигналов, которые процесс в настоящее время заблокировал. Сила POSIX API в том, что маской сигналов процесса можно манипулировать атомарно, как единым целым.
Маска сигналов процесса программно представляется с помощью набора сигналов. Это тип sigset_t. Концептуально он представляет собой просто битовую маску, причем значения 0 и 1 представляют отсутствие или наличие определенного сигнала в маске.
/* Непосредственное манипулирование маской сигналов. НЕ ДЕЛАЙТЕ ЭТОГО! */
int mask = (1 << SIGHUP) | (1 << SIGINT);
/* битовая маска для SIGHUP и SIGINT */
Однако, поскольку в системе может быть больше сигналов, чем может содержаться в одной int или long и поскольку интенсивное использование побитовых операций тяжело для восприятия, для управления наборами сигналов существует несколько функций API.
#include <signal.h> /* POSIX */
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 не содержит сигналов. Возвращает 0 в случае успеха и -1 при ошибке.
int sigfillset(sigset_t *set)
Полностью заполняет набор сигналов. По возвращении *set содержит все сигналы, определенные системой. Возвращает 0 в случае успеха и -1 при ошибке.
int sigaddset(sigset_t *set, int signum)
Добавляет signum к маске сигналов процесса в *set. Возвращает 0 в случае успеха и -1 при ошибке.
int sigdelset(sigset_t *set, int signum)
Удаляет signum из маски сигналов процесса в *set. Возвращает 0 в случае успеха и -1 при ошибке.
int sigismember(const sigset_t *set, int signum)
Возвращает true/false, если signum присутствует или не присутствует в *set.
Перед выполнением с переменной sigset_t каких-то действий всегда следует вызывать одну из функций sigemptyset() или sigfillset(). Существуют оба интерфейса, поскольку иногда бывает нужно начать с пустого набора и работать потом лишь с одним или двумя сигналами, а в другое время бывает нужно работать со всеми сигналами, возможно, убирая один или два сигнала.
10.6.3. Управление маской сигналов: sigprocmask() и др.
Маска сигналов процесса вначале пуста - заблокированных сигналов нет. (Это упрощение; см. раздел 10.9 «Сигналы, передающиеся через fork() и exec().) Три функции позволяют работать непосредственно с маской сигналов процесса:
#include <signal.h> /* POSIX */
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *set);
Функции следующие:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
Если oldset не равен NULL, получается маска сигналов текущего процесса и помещается в *oldset. Затем маска сигналов процесса обновляется в соответствии с содержимым set и значением how, который должен иметь одно из следующих значений:
SIG_BLOCK Объединить сигналы в *set с маской сигналов текущего процесса. Новая маска является объединением текущей маски и *set.
SIG_UNBLOCK Удалить сигналы в *set из маски сигналов процесса. Это не представляет проблемы, если *set содержит сигнал, который не содержится в текущей маске сигналов процесса.
SIG_SETMASK Заменить маску сигналов процесса содержимым *set.
Если set равен NULL, a oldset — нет, значение how неважно. Эта комбинация получает маску сигналов текущего процесса, не меняя ее. (Это явно выражено в стандарте POSIX, но не ясно из справочной страницы GNU/Linux.)
int sigpending(sigset_t *set)
Эта функция позволяет увидеть, какие сигналы ожидают решения; т.е. *set заполнен этими сигналами, которые были посланы, но они еще не доставлены, поскольку заблокированы.
int sigsuspend(const sigset_t *set)
Эта функция временно заменяет маску сигналов процесса содержимым *set, а затем приостанавливает процесс, пока сигнал не будет получен. По определению, заставить функцию вернуться может только сигнал, не находящийся в *set (см. раздел 10.7 «Сигналы для межпроцессного взаимодействия).
10.6.4. Перехват сигналов: sigaction()
Наконец мы готовы взглянуть на функцию sigaction(). Эта функция сложна, и мы намеренно опускаем множество деталей, которые предназначены для специального использования. Стандарт POSIX и справочная страница sigaction(2) предоставляют все подробности, хотя вы должны тщательно прочесть и то, и другое, чтобы полностью все усвоить.
#include <signal.h> /* POSIX */
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Аргументы следующие:
int signum
Интересующий сигнал, как в случае с другими функциями обработки сигналов.
const struct sigaction *act
Определение нового обработчика для сигнала signum.
struct sigaction *oldact
Определение текущего обработчика. Если не NULL, система до установки *act заполняет *oldact. *act может быть NULL, в этом случае *oldact заполняется, но больше ничего не меняется.
Таким образом, sigaction() и устанавливает новый обработчик, и получает старый за одно действие. struct sigaction выглядит следующим образом.
/* ПРИМЕЧАНИЕ: Порядок в структуре может варьировать. Могут быть
также и другие поля! */
struct sigaction {
sigset_t sa_mask; /* Дополнительные сигналы для блокирования */
int sa_flags; /* Контролирует поведение */
void (*sa_handler)(int);
/* Может образовать объединение с sa_sigaction */
void (*sa_sigaction)(int, siginfo_t*, void*);
/* Может образовать объединение с sa_handler */
}
Поля следующие:
sigset_t sa_mask
Набор дополнительных сигналов для блокирования при запуске функции обработчика. Таким образом, когда вызывается обработчик, общий набор заблокированных сигналов является объединением сигналов в маске процесса, сигналов в act->mask и, если SA_NODEFER сброшен, signum.
int sa_flags