Linux программирование в примерах
10.6. Сигналы POSIX
API POSIX основан на API
из BSD 4.2 и 4.3. С небольшими изменениями этот API можно было отнести к возможностям API как V7, так и System V Release 3. POSIX сделал эти изменения и переименовал APIsigvec(). Поскольку интерфейсsigaction()широко не использовался, мы не будем его описывать. Вместо этого в данном разделе описывается толькоsigvec(), который вы и должны так или иначе использовать. (На самом деле руководства BSD 4.4 от 1994 г. помечаютsigaction()как устаревшую, указывая читателю наsigvec().)sigaction()10.6.1. Обнажение проблемы
Что неладно с API System V Release 3? В конце концов, они предоставляют блокирование сигналов, так, что сигналы не теряются, и любой данный сигнал может быть надежно обработан.
Ответ в том, что этот API работает лишь с одним сигналом в одно и то же время. Программы обычно обрабатывают больше одного сигнала. И когда вы находитесь в середине процесса обработки одного сигнала, вы не хотите беспокоиться по поводу обработки еще и второго. (Предположим, вы только что начали отвечать по офисному телефону, когда зазвонил ваш мобильный телефон: вы бы предпочли, чтобы телефонная система ответила вызывающему, что в данный момент вы находитесь на другой линии и что скоро освободитесь, вместо того, чтобы проделывать все это самому.)
С API
каждый обработчик сигнала должен был бы временно блокировать все другие сигналы, сделать свою работу, а затем разблокировать их. Проблема в том, что в промежутке между любыми двумя вызовамиsigset()может появиться еще не заблокированный сигнал. Сценарий, еще раз, распространенный, создающий условия гонки.sighold()Решением является обеспечение возможности автоматической работы с группами сигналов, т.е. с помощью одного системного вызова. Вы достигаете этого, работая с наборами сигналов и маской сигналов процесса.
10.6.2. Наборы сигналов:
и связанные функцииsigset_tМаска сигналов процесса является списком сигналов, которые процесс в настоящее время заблокировал. Сила POSIX API в том, что маской сигналов процесса можно манипулировать атомарно, как единым целым.
Маска сигналов процесса программно представляется с помощью набора сигналов. Это тип
. Концептуально он представляет собой просто битовую маску, причем значения 0 и 1 представляют отсутствие или наличие определенного сигнала в маске.sigset_t/* Непосредственное манипулирование маской сигналов. НЕ ДЕЛАЙТЕ ЭТОГО! */int mask = (1 << SIGHUP) | (1 << SIGINT);/* битовая маска для SIGHUP и SIGINT */Однако, поскольку в системе может быть больше сигналов, чем может содержаться в одной
илиintи поскольку интенсивное использование побитовых операций тяжело для восприятия, для управления наборами сигналов существует несколько функций API.long#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)Освобождает набор сигналов. По возвращении
не содержит сигналов. Возвращает 0 в случае успеха и -1 при ошибке.*setint sigfillset(sigset_t *set)Полностью заполняет набор сигналов. По возвращении
содержит все сигналы, определенные системой. Возвращает 0 в случае успеха и -1 при ошибке.*setint sigaddset(sigset_t *set, int signum)Добавляет
к маске сигналов процесса вsignum. Возвращает 0 в случае успеха и -1 при ошибке.*setint sigdelset(sigset_t *set, int signum)Удаляет
из маски сигналов процесса вsignum. Возвращает 0 в случае успеха и -1 при ошибке.*setint 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