UNIX: разработка сетевых приложений
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. Эта функция POSIX, сохранив в одной из локальных переменных текущую маску сигналов, присваивает текущей маске значение аргументаsigsuspend. Так какzeromaskявляется пустым набором сигналов, то разрешается доставка любых сигналов. Как только перехватывается сигнал и завершается обработчик, функцияzeromaskтакже завершается. (Это необычная функция, поскольку она всегда возвращает ошибкуsigsuspend.) Прежде чем завершиться, функцияEINTRвсегда устанавливает такое значение маски сигналов, которое предшествовало ее вызову (в данном случаеsigsuspend). Таким образом гарантируется, что, когда функцияnewmaskвозвращает значение, сигналsigsuspendблокирован. Именно поэтому можно проверять счетчикSIGIO, поскольку известно, что пока он проверяется, сигналnqueueне может быть доставлен.SIGIOПРИМЕЧАНИЕА что произойдет, если сигнал SIGIO не будет блокирован во время проверки переменной nqueue, используемой совместно основным циклом и обработчиком сигналов? Может случиться так, что проверка nqueue покажет нулевое значение, а сразу после проверки возникнет сигнал и nqueue станет равна 1. Далее мы вызовем функцию sigsuspend и перейдем в режим ожидания, в результате чего пропустим сигнал. После вызова функции sigsuspend мы не выйдем из режима ожидания, пока не поступит другой сигнал. Это похоже на ситуацию гонок, описанную в разделе 20.5
Разблокирование SIGIO и отправка ответаРазблокируем сигнал SIGIO с помощью вызова46-51, чтобы вернуть маске сигналов процесса значение, сохраненное ранее (sigprocmask). В этом случае ответ посылается с помощью функцииoldmask. Индексsendtoувеличился на 1, и если его значение совпадает с количеством элементов массива, он снова обнуляется. Массив используется как кольцевой буфер. Обратите внимание, что нет необходимости блокировать сигналigetво время изменения переменнойSIGIO, поскольку этот индекс используется только в основном цикле и никогда не изменяется обработчиком сигнала.igetБлокирование SIGIOСигнал52-54блокируется, а значение переменнойSIGIOуменьшается на 1. Во время изменения данной переменной необходимо заблокировать сигнал, поскольку она используется совместно основным циклом и обработчиком сигнала. Также необходимо, чтобы сигналnqueueбыл заблокирован, когда в начале цикла происходит проверка переменнойSIGIO.nqueue