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 Отправить сигнал 44 с данными 123 в PID 6267

./t_sigqueue: PID is 6269, UID is 1000

./signalfd_sigval: got signal 44; ssi_pid=6269; ssi_int=123

$ kill %1 Завершить программу, запущенную в фоновом режиме

22.12. Межпроцессное взаимодействие посредством сигналов

С одной точки зрения мы можем рассматривать сигналы как форму межпроцессного взаимодействия (IPC). Однако в таком виде сигналы страдают от большого количества ограничений. Во-первых, по сравнению с другими методами взаимодействия процессов, которые мы рассматриваем в следующих главах, программирование на сигналах сложно и громоздко. Этому есть следующие причины.

• Асинхронная природа сигналов означает, что появляется вероятность столкновения с проблемами, в том числе с требованиями реентерабельности, состоянием гонки, а также с проблемой корректной обработки глобальных переменных из обработчиков сигналов. (Вероятность возникновения большинства этих проблем снимается при использовании функций sigwaitinfo() и signalfd() для синхронного получения сигналов.)

• Стандартные сигналы не ставятся в очередь. Даже для сигналов реального времени существуют пределы по количеству сигналов, которые могут быть поставлены в очередь. Это значит, что во избежание потери информации процесс, получающий сигналы, должен иметь метод информирования отправителя о том, что он (получатель) готов к приему следующего сигнала. Самый очевидный способ решения этой проблемы — отправка получателем сигнала отправителю.

Еще одна, более сложная, проблема заключается в том, что сигналы могут передавать только ограниченный объем информации: номер сигнала и — в случае с сигналами реального времени — слово (целое число или указатель) дополнительных данных. Эта низкая пропускная способность делает сигналы медленными по сравнению с другими методами IPC, например такими, как каналы.

По причине вышеперечисленных ограничений сигналы редко используются для реализации межпроцессного взаимодействия.

22.13. Ранние API сигналов

В нашем обсуждении сигналов мы сосредоточились на API сигналов POSIX. Сейчас настало время коротко обсудить исторические API, предоставлявшиеся в системе BSD. Несмотря на то что во всех современных приложениях должны использоваться POSIX- интерфейсы, мы можем встретиться с этими устаревшими версиями при переносе (как правило, старых) приложений из других реализаций UNIX. Так как Linux предоставляет (как и многие другие реализации UNIX) совместимость с BSD, зачастую все, что нам нужно для переноса старых интерфейсов, — это перекомпилировать их под Linux.

В основе API сигналов POSIX лежат интерфейсы 4.2BSD, поэтому функции POSIX — это прямые аналоги функций BSD.

Я приведу прототипы функций в интерфейсах BSD, а затем вкратце объясню работу каждой функции. Более подробная информация представлена на страницах справочника.

#define _BSD_SOURCE

#include

int sigvec(int sig, const struct sigvec *vec, struct sigvec *ovec);

Перейти на страницу:

Похожие книги