33 Signal(SIGHUP, sig_hup);
34 Signal(SIGIO, sig_io);
35 Fcntl(sockfd, F_SETOWN, getpid());
36 Ioctl(sockfd, FIOASYNC, &on);
37 Ioctl(sockfd. FIONBIO, &on);
38 Sigemptyset(&zeromask); /* инициализация трех наборов сигналов */
39 Sigemptyset(&oldmask);
40 Sigemptyset(&newmask);
41 Sigaddset(&newmask, SIGIO); /* сигнал, который хотим блокировать*/
42 Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
43 for (;;) {
44 while (nqueue == 0)
45 sigsuspend(&zeromask); /* ждем дейтаграмму для обработки */
46 /* разблокирование SIGIO */
47 Sigprocmask(SIG_SETMASK, &oldmask, NULL);
48 Sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, 0,
49 dg[iget].dg_sa, dg[iget].dg_salen);
50 if (++iget >= QSIZE)
51 iget = 0;
52 /* блокировка SIGIO */
53 Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
54 nqueue--;
55 }
56 }
Инициализация очереди принятых дейтаграмм27-32 Дескриптор сокета сохраняется в глобальной переменной, поскольку он необходим обработчику сигналов. Происходит инициализация очереди принятых дейтаграмм.
Установка обработчиков сигналов и флагов сокетов33-37 Для сигналов SIGHUP (он используется для диагностических целей) и SIGIO устанавливаются обработчики. С помощью функции fcntl задается владелец сокета, а с помощью функции ioctl устанавливаются флаги ввода-вывода, управляемого сигналом, и неблокируемого ввода-вывода.
ПРИМЕЧАНИЕРанее отмечалось, что для разрешения ввода-вывода, управляемого сигналом, в POSIX применяется флаг O_ASYNC функции fcntl, но поскольку большинство систем пока его не поддерживают, мы используем функцию ioctl. Поскольку большинство систем не поддерживают флаг O_NONBLOCK для включения неблокируемого ввода-вывода, здесь также рассмотрен вариант использования функции ioctl.
Инициализация наборов сигналов38-41 Инициализируется три набора сигналов: zeromask (никогда не изменяется), oldmask (хранит старую маску сигнала, когда SIGIO блокируется) и newmask. Функция sigaddset включает в набор newmask бит, соответствующий SIGIO.
Блокирование SIGIO и ожидание дальнейших действий42-45 Функция sigprocmask сохраняет текущую маску сигналов процесса в oldmask, а затем выполняет логическое сложение, сравнивая newmask с текущей маской сигналов. Такие действия блокируют сигнал SIGIO и возвращают текущую маску сигналов. Далее мы заходим в цикл for и проверяем счетчик nqueue. Пока этот счетчик равен нулю, ничего делать не нужно, и мы вызываем функцию sigsuspend. Эта функция POSIX, сохранив в одной из локальных переменных текущую маску сигналов, присваивает текущей маске значение аргумента zeromask. Так как zeromask является пустым набором сигналов, то разрешается доставка любых сигналов. Как только перехватывается сигнал и завершается обработчик, функция sigsuspend также завершается. (Это необычная функция, поскольку она всегда возвращает ошибку EINTR.) Прежде чем завершиться, функция sigsuspend всегда устанавливает такое значение маски сигналов, которое предшествовало ее вызову (в данном случае newmask). Таким образом гарантируется, что, когда функция sigsuspend возвращает значение, сигнал SIGIO блокирован. Именно поэтому можно проверять счетчик nqueue, поскольку известно, что пока он проверяется, сигнал SIGIO не может быть доставлен.
ПРИМЕЧАНИЕА что произойдет, если сигнал SIGIO не будет блокирован во время проверки переменной nqueue, используемой совместно основным циклом и обработчиком сигналов? Может случиться так, что проверка nqueue покажет нулевое значение, а сразу после проверки возникнет сигнал и nqueue станет равна 1. Далее мы вызовем функцию sigsuspend и перейдем в режим ожидания, в результате чего пропустим сигнал. После вызова функции sigsuspend мы не выйдем из режима ожидания, пока не поступит другой сигнал. Это похоже на ситуацию гонок, описанную в разделе 20.5
Разблокирование SIGIO и отправка ответа46-51 Разблокируем сигнал SIGIO с помощью вызова sigprocmask, чтобы вернуть маске сигналов процесса значение, сохраненное ранее (oldmask). В этом случае ответ посылается с помощью функции sendto. Индекс iget увеличился на 1, и если его значение совпадает с количеством элементов массива, он снова обнуляется. Массив используется как кольцевой буфер. Обратите внимание, что нет необходимости блокировать сигнал SIGIO во время изменения переменной iget, поскольку этот индекс используется только в основном цикле и никогда не изменяется обработчиком сигнала.
Блокирование SIGIO52-54 Сигнал SIGIO блокируется, а значение переменной nqueue уменьшается на 1. Во время изменения данной переменной необходимо заблокировать сигнал, поскольку она используется совместно основным циклом и обработчиком сигнала. Также необходимо, чтобы сигнал SIGIO был заблокирован, когда в начале цикла происходит проверка переменной nqueue.