Книга: UNIX: взаимодействие процессов

Пример: уведомление с использованием sigwait вместо обработчика

Пример: уведомление с использованием sigwait вместо обработчика

Хотя программа из предыдущего примера работает правильно, можно повысить ее эффективность. Программа использует sigsuspend для блокировки в ожидании прихода сообщения. При помещении сообщения в пустую очередь вызывается сигнал, основной поток останавливается, запускается обработчик, который устанавливает флаг mqflag, затем снова запускается главный поток, он обнаруживает, что значение mqflag отлично от нуля, и считывает сообщение. Более простой и эффективный подход заключается в блокировании в функции, ожидающей получения сигнала, что не требует вызова обработчика только для установки флага. Эта возможность предоставляется функцией sigwait:

#include <signal.h>
int sigwait(const sigset_t *set, int *sig);
/* Возвращает 0 в случае успешного завершения, –1 – в случае ошибки */

Перед вызовом sigwait мы блокируем некоторые сигналы. Набор блокируемых сигналов указывается в качестве аргумента set. Функция sigwait блокируется, пока не придет по крайней мере один из этих сигналов. Когда он будет получен, функция возвратит его. Значение этого сигнала сохраняется в указателе sig, а функция возвращает значение 0. Это называется синхронным ожиданием асинхронного события: мы используем сигнал, но не пользуемся асинхронным обработчиком сигнала.

В листинге 5.11 приведен текст программы, использующей mq_notifу и sigwait.

Листинг 5.11. Использование mq_notify совместно с sigwait

//pxmsg/mqnotifysig4.c
1  #include "unpipc.h"
2  int
3  main(int argc, char **argv)
4  {
5   int signo;
6   mqd_t mqd;
7   void *buff;
8   ssize_t n;
9   sigset_t newmask;
10  struct mq_attr attr;
11  struct sigevent sigev;
12  if (argc != 2)
13   err_quit("usage: mqnotifysig4 <name>");
14  /* открытие очереди, получение атрибутов, выделение буфера */
15  mqd = Mq_open(argv[1], O_RDONLY | O_NONBLOCK);
16  Mq_getattr(mqd, &attr);
17  buff = Malloc(attr.mq_msgsize);
18  Sigemptyset(&newmask);
19  Sigaddset(&newmask, SIGUSR1);
20  Sigprocmask(SIG_BLOCK, &newmask, NULL); /* блокируем SIGUSR1 */
21  /* установка обработчика, включение уведомления */
22  sigev.sigev_notify = SIGEV_SIGNAL;
23  sigev.sigev_signo = SIGUSR1;
24  Mq_notify(mqd, &sigev);
25  for (;;) {
26   Sigwait(&newmask, &signo);
27   if (signo == SIGUSR1) {
28    Mq_notify(mqd, &sigev); /* перерегистрируемся */
29    while ((n = mq_receive(mqd, buff, attr.mq_msgsize, NULL)) >= 0) {
30     printf("read %ld bytesn", (long) n);
31    }
32    if (errno != EAGAIN)
33     err_sys("mq_receive error");
34   }
35  }
36  exit(0);
37 }

Инициализация набора сигналов и блокировка SIGUSR1

18-20 Инициализируется один набор сигналов, содержащий только SIGUSR1, а затем этот сигнал блокируется sigprocmask.

Ожидание сигнала

26-34 Мы блокируем выполнение программы и ждем прихода сигнала, вызвав sigwait. При получении сигнала SIGUSR1 мы перерегистрируемся на уведомление и считываем все доступные сообщения.

ПРИМЕЧАНИЕ

Функция sigwait часто используется в многопоточных процессах. Действительно, глядя на прототип функции, мы можем заметить, что возвращаемое значение будет 0 или одной из ошибок Еххх, что весьма похоже на функции Pthread. Однако в многопоточном процессе нельзя пользоваться sigprocmask — вместо нее следует вызывать pthread_ sigmask, которая изменяет маску сигналов только для вызвавшего ее потока. Аргументы pthread_sigmask совпадают с аргументами sigprocmask.

Существуют два варианта функции sigwait: sigwaitinfo возвращает структуру siginfo_t (которая будет определена в следующем разделе) и предназначена для использования с надежными сигналами; функция sigtimedwait также возвращает структуру siginfo_t и позволяет вызывающему процессу установить ограничение по времени на ожидание.

Большая часть книг о многопоточном программировании, таких как [3], рекомендуют пользоваться sigwait для обработки всех сигналов в многопоточном процессе и не использовать асинхронные обработчики. 

Оглавление книги


Генерация: 0.739. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз