ПРИМЕЧАНИЕ
С сигналами в Unix всегда была связана одна проблема: действие сигнала сбрасывалось на установленное по умолчанию каждый раз при отправке сигнала (раздел 10.4 [21]). Обычно первой функцией, вызываемой обработчиком сигнала, была signal, переопределявшая обработчик. Это создавало небольшой временной промежуток между отправкой сигнала и переопределением обработчика, в который процесс мог быть завершен при повторном появлении того же сигнала. На первый взгляд может показаться, что та же проблема должна возникать и при использовании mq_notify, поскольку процесс должен перерегистрироваться каждый раз после появления уведомления. Однако очереди сообщений отличаются по своим свойствам от сигналов, поскольку необходимость отправки уведомления не может возникнуть, пока очередь не будет пуста. Следовательно, необходимо аккуратно перерегистрироваться на получение уведомления
Пример: простая программа с уведомлением
Прежде чем углубляться в тонкости сигналов реального времени и потоков Posix, мы напишем простейшую программу, включающую отправку сигнала SI6USR1 при помещении сообщения в пустую очередь. Эта программа приведена в листинге 5.8, и мы отметим, что она содержит ошибку, о которой мы вскоре поговорим подробно.
//pxmsg/mqnotifysigl.c
1 #include "unpipc.h"
2 mqd_t mqd;
3 void *buff;
4 struct mq_attr attr;
5 struct sigevent sigev;
6 static void sig_usrl(int);
7 int
8 main(int argc, char **argv)
9 {
10 if (argc != 2)
11 err_quit("usage: mqnotifysig1
12 /* открываем очередь, получаем атрибуты, выделяем буфер */
13 mqd = Mq_open(argv[1], O_RDONLY);
14 Mq_getattr(mqd, &attr);
15 buff = Malloc(attr.mq_msgsize);
16 /* устанавливаем обработчик, включаем уведомление */
17 Signal(SIGUSR1, sig_usr1);
18 sigev.sigev_notify = SIGEV_SIGNAL;
19 sigev.sigev_signo = SIGUSR1;
20 Mq_notify(mqd, &sigev);
21 for (;;)
22 pause(); /* все делает обработчик */
23 exit(0);
24 }
25 static void
26 sig_usr1(int signo)
27 {
28 ssize_t n;
29 Mq_notify(mqd, &sigev); /* сначала перерегистрируемся */
30 n = Mq_receive(mqd, buff, attr.mq_msgsize, NULL);
31 printf("SIGUSR1 received, read %ld bytes\n", (long) n);
32 return;
33 }
2-6 Мы объявляем несколько глобальных переменных, используемых совместно функцией main и нашим обработчиком сигнала (sig_usr1).
12-15 Мы открываем очередь сообщений, получаем ее атрибуты и выделяем буфер считывания соответствующего размера.
16-20 Сначала мы устанавливаем свой обработчик для сигнала SIGUSR1. Мы присваиваем полю sigev_notify структуры sigevent значение SIGEV_SIGNAL, что говорит системе о необходимости отправки сигнала, когда очередь из пустой становится непустой. Полю sigev_signo присваивается значение, соответствующее тому сигналу, который мы хотим получить. Затем вызывается функция mq_notify.