printf("%s: PID = %ld\n", argv[0], (long) getpid());
sigemptyset(&mask);
for (j = 1; j < argc; j++)
sigaddset(&mask, atoi(argv[j]));
if (sigprocmask(SIG_BLOCK, &mask, NULL) == –1)
errExit("sigprocmask");
sfd = signalfd(–1, &mask, 0);
if (sfd == –1)
errExit("signalfd");
for (;;) {
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
if (s!= sizeof(struct signalfd_siginfo))
errExit("read");
printf("%s: got signal %d", argv[0], fdsi.ssi_signo);
if (fdsi.ssi_code == SI_QUEUE) {
printf("; ssi_pid = %d; ", fdsi.ssi_pid);
printf("ssi_int = %d", fdsi.ssi_int);
}
printf("\n");
}
}
signals/singalfd_sigval.c
За файловым дескриптором signalfd можно осуществлять мониторинг так же, как и за остальными дескрипторами, с помощью функций select(), poll() и epoll() (описаны в главе 59). Кроме прочего, этот механизм также предоставляет альтернативу трюку с зацикленным каналом, описанному в подразделе 59.5.2. Если сигналы находятся в режиме ожидания, то эти техники помечают файловый дескриптор как доступный для чтения.
Когда дескриптор signalfd больше не нужен, следует закрыть его, чтобы освободить соответствующий ресурс ядра.
Использование функции signalfd() продемонстрировано в листинге 22.7. Эта программа создает маску из номеров сигналов, указанных в качестве аргументов командой строки, блокирует эти сигналы, а затем создает файловый дескриптор signalfd для чтения этих сигналов. Затем программа переходит в цикл, считывая сигналы из файлового дескриптора, и выводит некоторую информацию из возвращенной структуры signalfd_siginfo. В следующей сессии оболочки мы запустили программу из листинга 22.7 в фоновом режиме, а затем отправили ей сигнал реального времени с сопровождающими данными с помощью программы из листинга 22.2 (t_sigqueue.c):
$ ./signalfd_sigval 44 &
./signalfd_sigval: PID = 6267
[1] 6267
$ ./t_sigqueue 6267 44 123
./t_sigqueue: PID is 6269, UID is 1000
./signalfd_sigval: got signal 44; ssi_pid=6269; ssi_int=123
$ kill %1
С одной точки зрения мы можем рассматривать сигналы как форму межпроцессного взаимодействия (IPC). Однако в таком виде сигналы страдают от большого количества ограничений. Во-первых, по сравнению с другими методами взаимодействия процессов, которые мы рассматриваем в следующих главах, программирование на сигналах сложно и громоздко. Этому есть следующие причины.
• Асинхронная природа сигналов означает, что появляется вероятность столкновения с проблемами, в том числе с требованиями реентерабельности, состоянием гонки, а также с проблемой корректной обработки глобальных переменных из обработчиков сигналов. (Вероятность возникновения большинства этих проблем снимается при использовании функций sigwaitinfo() и signalfd() для синхронного получения сигналов.)
• Стандартные сигналы не ставятся в очередь. Даже для сигналов реального времени существуют пределы по количеству сигналов, которые могут быть поставлены в очередь. Это значит, что во избежание потери информации процесс, получающий сигналы, должен иметь метод информирования отправителя о том, что он (получатель) готов к приему следующего сигнала. Самый очевидный способ решения этой проблемы — отправка получателем сигнала отправителю.
Еще одна, более сложная, проблема заключается в том, что сигналы могут передавать только ограниченный объем информации: номер сигнала и — в случае с сигналами реального времени — слово (целое число или указатель) дополнительных данных. Эта низкая пропускная способность делает сигналы медленными по сравнению с другими методами IPC, например такими, как каналы.
По причине вышеперечисленных ограничений сигналы редко используются для реализации межпроцессного взаимодействия.
В нашем обсуждении сигналов мы сосредоточились на API сигналов POSIX. Сейчас настало время коротко обсудить исторические API, предоставлявшиеся в системе BSD. Несмотря на то что во всех современных приложениях должны использоваться POSIX- интерфейсы, мы можем встретиться с этими устаревшими версиями при переносе (как правило, старых) приложений из других реализаций UNIX. Так как Linux предоставляет (как и многие другие реализации UNIX) совместимость с BSD, зачастую все, что нам нужно для переноса старых интерфейсов, — это перекомпилировать их под Linux.
В основе API сигналов POSIX лежат интерфейсы 4.2BSD, поэтому функции POSIX — это прямые аналоги функций BSD.
Я приведу прототипы функций в интерфейсах BSD, а затем вкратце объясню работу каждой функции. Более подробная информация представлена на страницах справочника.
#define _BSD_SOURCE
#include
int sigvec(int