Читать «UNIX: взаимодействие процессов» онлайн - страница 69

Уильям Ричард Стивенс

fork          read      stat

Пример: уведомление сигналом

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

Глобальная переменная

2 Поскольку единственное действие, выполняемое обработчиком сигнала, заключается в присваивании ненулевого значения флагу mqflag, глобальным переменным из листинга 5.8 уже не нужно являться таковыми. Уменьшение количества глобальных переменных — это всегда благо, особенно при использовании программных потоков.

Открытие очереди сообщений

15-18 Мы открываем очередь сообщений, получаем ее атрибуты и выделяем буфер считывания.

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

19-22 Мы инициализируем три набора сигналов и устанавливаем бит для сигнала SIGUSR1 в наборе newmask.

Установка обработчика сигнала, включение уведомления

23-27 Мы устанавливаем обработчик сигнала для SIGUSR1, присваиваем значения полям структуры sigevent и вызываем mq_notify. 

Листинг 5.9. Обработчик сигнала устанавливает флаг для главного потока (неправильная версия)

//pxmsg/mqnotifysig2.c

1  #include "unpipc.h"

2  volatile sig_atomic_t mqflag; /* ненулевое значение устанавливается обработчиком */

3  static void sig_usrl(int);

4  int

5  main(int argc, char **argv)

6  {

7   mqd_t mqd;

8   void *buff;

9   ssize_t n;

10  sigset_t zeromask, newmask, oldmask;

11  struct mq_attr attr;

12  struct sigevent sigev;

13  if (argc != 2)

14   err_quit("usage: mqnotifysig2 <name>");

15  /* открытие очереди, получение атрибутов, выделение буфера */

16  mqd = Mq_open(argv[1], O_RDONLY);

17  Mq_getattr(mqd, &attr);

18  buff = Malloc(attr.mq_msgsize);

19  Sigemptyset(&zeromask); /* сигналы не блокируются */

20  Sigemptyset(&newmask);

21  Sigemptyset(&oldmask);

22  Sigaddset(&newmask, SIGUSR1);

23  /* установка обработчика, включение уведомления */

24  Signal(SIGUSR1, sig_usr1);

25  sigev.sigev_notify = SIGEV_SIGNAL;

26  sigev.sigev_signo = SIGUSR1;

27  Mq_notify(mqd, &sigev);

28  for (;;) {

29   Sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* блокируем SIGUSR1 */

30   while (mqflag == 0)

31    sigsuspend(&zeromask);

32   mqflag = 0; /* сброс флага */

33   Mq_notify(mqd, &sigev); /* перерегистрируемся */

34   n = Mq_receive(mqd, buff, attr.mq_msgsize, NULL);

35   printf("read %ld bytes\n", (long) n);

36   Sigprocmask(SIG_UNBLOCK, &newmask, NULL); /* разблокируем SIGUSR1 */

37  }

38  exit(0);

39 }

40 static void

41 sig_usr1(int signo)

42 {

43  mqflag = 1;

44  return;

45 } 

Ожидание установки флага обработчиком

28-32 Мы вызываем sigprocmask, чтобы заблокировать SIGUSR1, сохраняя текущую маску сигналов в oldmask. Затем мы в цикле проверяем значение глобального флага mqflag, ожидая, когда обработчик сигнала установит его в ненулевое значение. Пока значение этого флага равно нулю, мы вызываем sigsuspend, что автоматически приостанавливает вызывающий поток и устанавливает его маску в zeromask (сигналы не блокируются). Раздел 10.16 [21] рассказывает о функции sigsuspend более подробно. Также там объясняются причины, по которым мы должны проверять значение переменной mqflag только при заблокированном сигнале SIGUSR1. Каждый раз при выходе из sigsuspend сигнал SIGUSR1 блокируется.