Запустим теперь эту программу в одном из окон
solaris % mqcreate /test1
solaris % mqnotifysig1 /test1
и затем выполним следующую команду в другом окне
solaris % mqsend /test1 50 16
Как и ожидалось, программа mqnotifysig1 выведет сообщение: SIGUSR1 received, read 50 bytes.
Мы можем проверить, что только один процесс может быть зарегистрирован на получение уведомления в любой момент, запустив копию пpoгрaммы в другом окне:
solaris % mqnotifysig1 /test1
mq_notify error: Device busy
Это сообщение соответствует коду ошибки EBUSY.
Сигналы Posix: функции типа Async-Signal-Safe
Недостаток пpoгрaммы из листинга 5.8 в том, что она вызывает mq_notify, mq_receive и printf из обработчика сигнала. Ни одну из этих функций вызывать оттуда не следует.
Функции, которые могут быть вызваны из обработчика сигнала, относятся к группе, называемой, согласно Posix, async-signal-safe functions (функции, обеспечивающие безопасную обработку асинхронных сигналов). В табл. 5.1 приведены эти функции по стандарту Posix вместе с некоторыми дополнительными, появившимися только в Unix 98.
Функции, которых нет в этом списке, не должны вызываться из обработчика сигнала. Обратите внимание, что в списке отсутствуют стандартные функции библиотеки ввода-вывода и функции pthread_XXX для работы с потоками. Из всех функций IPC, рассматриваемых в этой книге, в список попали только sem_post, read и write (подразумевается, что последние две используются с программными каналами и FIFO).
ПРИМЕЧАНИЕ
Стандарт ANSI С указывает четыре функции, которые могут быть вызваны из обработчика сигналов: abort, exit, longjmp, signal. Первые три отсутствуют в списке функций async-signal-safe стандарта Unix 98.
Таблица 5.1. Функции, относящиеся к группе async-signal-safe
access fpathconf rename sysconf
aio_return fstat rmdir tcdrain
aio_suspend fsync sem_post tcflow
alarm getegid setgid tcflush
cfgetispeed geteuid setpgid tcgetattr
cfgetospeed getgid setsid tcgetgrp
cfsetispeed getgroups setuid tcsendbreak
cfsetospeed getpgrp sigaction tcsetattr
chdir getpid sigaddset tcsetpgrp
chmod getppid sigdelset time
chown getuid sigemptyset timer_getoverrun
clock_gettime kill sigfillset timer_gettime
close link sigismember timer_settime
creat lseek signal times
dup mkdir sigpause umask
dup2 mkfifo sigpending uname
execle open sigprocmask unlink
execve pathconf sigqueue utime
_exit pause sigset wait
fcntl pipe sigsuspend waitpid
fdatasync raise sleep write
fork read stat
Пример: уведомление сигналом
Одним из способов исключения вызова каких-либо функций из обработчика сигнала является установка этим обработчиком глобального флага, который проверяется программным потоком для получения информации о приходе сообщения. В листинге 5.9 иллюстрируется этот метод, хотя новая программа также содержит ошибку, но уже другую, о которой мы вскоре поговорим подробнее.
2 Поскольку единственное действие, выполняемое обработчиком сигнала, заключается в присваивании ненулевого значения флагу mqflag, глобальным переменным из листинга 5.8 уже не нужно являться таковыми. Уменьшение количества глобальных переменных — это всегда благо, особенно при использовании программных потоков.
15-18 Мы открываем очередь сообщений, получаем ее атрибуты и выделяем буфер считывания.
19-22 Мы инициализируем три набора сигналов и устанавливаем бит для сигнала SIGUSR1 в наборе newmask.
23-27 Мы устанавливаем обработчик сигнала для SIGUSR1, присваиваем значения полям структуры sigevent и вызываем mq_notify.
//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 }