С каждым процессом связан аттрибут, который не наследуется при fork
, - это маска сигналов, ожидающих доставки. Как правило, она представляется внутри системы в виде целого числа, хотя стандартом внутреннее представление не регламентируется. Отдельные биты в этой маске соответствуют отдельным сигналам, которые были отправлены процессу, но ещё не обработаны.
Поскольку одним битом можно закодировать только бинарное значение, то учитывается только сам факт поступления сигнала, но не их количество. Например, это может быть критичным, если сигналы долго не обрабатываются. Таким образом, использовать механизм стандартных сигналов для синхронизации двух процессов - нельзя.
Тот факт, что сигнал оказался в маске ожидающих доставки, ещё не означает, что он будет немедленно обработан. У процесса (или даже у отдельной нити) может существовать маска заблокированных сигналов, которая накладывается на маску ожидающих доставки с помощью поразрядной операции И-НЕ
.
В отличии от маски ожидающих достаки, маска заблокированных сигналов наследуется при fork
.
Множества сигналов описываются типом данных sigset_t
, объявленным в заголовочном файле <signal.h>
.
Операции над множествами:
sigemptyset(sigset_t *set)
- инициализировать пустое множество;sigfillset(sigset_t *set)
- инициализировать полное множество;sigaddset(sigset_t *set, int signum)
- добавить сигнал к множеству;sigdelset(sigset_t *set, int signum)
- убрать сигнал из множества;sigismember(sigset_t *set, int signum)
- проверить наличие сигнала в множестве.
Временная блокировка доставки сигналов часто используется для защиты критических секций программы, когда внезапное выполнение обработчика может повредить целостности данных или нарушению логики поведения.
При этом, нельзя заблокировать сигналы SIGSTOP
и SIGKILL
.
Блокировка реализуется установки маски блокируемых сигналов с помощью системного вызова sigprocmask
:
int sigprocmask(int how, sigset_t *set, sigset_t *old_set);
где old_set
- куда записать старую маску (может быть NULL
, если не интересно), а параметр how
- это одно из значений:
SIG_SETMASK
- установить множество сигналов в качестве маски блокируемых сигналов;SIG_BLOCK
- добавить множество к маске блокируемых сигналов;SIG_UNBLOCK
- убрать множество из маски блокируемых сигналов.
Сигналы, которые попали в маску сигналов, ожидающих доставки, остаются там до тех пор, пока не будут доставлены (а в дальнейшем - либо игнорированы, либо обработаны). Если сигнал был заблокирован, то его обработчик будет вызван сразу после разблокировки.
#include <signal.h>
#include <unistd.h>
static void
handler(int signum) {
static const char Message[] = "Got Ctrl+C\n";
write(1, Message, sizeof(Message)-1);
}
int main() {
sigaction(SIGINT,
&(struct sigaction)
{.sa_handler=handler, .sa_flags=SA_RESTART},
NULL);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
while (1) {
sigprocmask(SIG_BLOCK, &mask, NULL);
sleep(10);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
}
}
В данном примере sigprocmask.c обработчик сигнала SIGINT
всё равно будет выполнен, даже несмотря на длительную паузу.
Маска сигналов может быть временно заменена.
Системный вызов sigsuspend(sigset_t *temp_mask)
временно приостанавливает работу программы до тех пор, пока не прийдёт один из сигналов, отсутсвующий в множестве temp_mask
. Сигналы, отсутсвующие в новом временном множестве, будут доставлены даже в том случае, если они ранее были заблокированы.
Сразу после завершения работы sigsuspend
, маска заблокированных сигналов вернется в исходную.
Одно из полей структуры sigaction
определяет маску сигналов, доставка которых будет заблокирована на время выполнения обработчика. Дополнительные флаги при этом не требуются.
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = handler;
act.sa_flags = SA_RESTART;
sigfillset(&act.sa_mask); // блокировать все сигналы
Сигналы реального времени - это расширение POSIX, которые, в отличии от стандартных UNIX-сигналов могут быть обработаны используя очередь доставки, и таким образом:
- учитывается их количество и порядок прихода;
- вместе с сигналом сохраняется дополнительная метаинформация, включая одно челочисленное поле, которое может быть использовано произвольным образом.
Сигналы реального времени задаются значениями от SIGRTMIN
до SIGRTMAX
, и могут быть использованы с помощью kill
как дополнительные стандартные UNIX-сигналы. Действие по умолчанию аналогично SIGTERM
.
Для использования очереди сигналов, необходимо отправлять их с помощью функции sigqueue
:
#include <signal.h>
union sigval {
int sival_int;
void* sival_ptr;
};
int sigqueue(pid_t pid, int signum, const union sigval value);
Эта функция может завершиться с ошибкой EAGAIN
в том случае, если исчерпан лимит на количество сигналов в очереди. Опциональное значение, передаваемое в качестве третьего параметра, может быть извлечено получателем из поля si_value
структуры siginfo_t
, если использовать вариант обработчика sigaction
с тремя аргументами.