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

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

36   }

37   if (errno != EAGAIN)

38    err_sys("mq_receive error");

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

40  }

41  exit(0);

42 }

43 static void

44 sig_usr1(int signo)

45 {

46  mqflag = 1;

47  return;

48 }

Открытие очереди сообщений в режиме отключенной блокировки

15-18 Первое изменение в программе: при открытии очереди сообщений указывается флаг O_NONBLOCK.

Считывание всех сообщений из очереди

34-38 Другое изменение: mq_receive вызывается в цикле, считывая все сообщения в очереди, пока не будет возвращена ошибка с кодом EAGAIN, означающая отсутствие сообщений в очереди. 

Пример: уведомление с использованием 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) {