UNIX: разработка сетевых приложений
Часть 224 из 399 Информация о книге
recvfrom_alarm(int signo) {had_alarm = 1;return;}Флаг сбрасывается в 0 каждый раз, когда вызывается функция
. Наша функцияalarmпроверяет этот флаг перед вызовом функцииdg_cliи не вызывает ее, если флаг ненулевой.recvfromfor (;;) {len = servlen;Sigprocmask(SIG_UNBLOCK, &sigset_alrm, NULL);if (had_alarm == 1)break;n = recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);Если сигнал был сгенерирован во время его блокирования (после предыдущего возвращения из функции
), то после разблокирования в этой части кода он будет доставлен перед завершением функцииrecvfrom, устанавливающей наш флаг. Однако между проверкой флага и вызовом функцииsigprocmaskсуществует промежуток времени, в течение которого сигнал может быть сгенерирован и доставлен, и если это произойдет, вызов функцииrecvfromзаблокируется навсегда (разумеется, мы считаем при этом, что не приходит никаких дополнительных ответов).recvfromБлокирование и разблокирование сигнала с помощью функции pselect
Одним из корректных решений будет использование функции
(см. раздел 6.9), как показано в листинге 20.3.pselectЛистинг 20.3. Блокирование и разблокирование сигналов с помощью функции pselect
//bcast/dgclibcast4.с1 #include "unp.h"2 static void recvfrom_alarm(int);3 void4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)5 {6 int n;7 const int on = 1;8 char sendline[MAXLINE], recvline[MAXLINE + 1];9 fd_set rset;10 sigset_t sigset_alrm, sigset_empty;11 socklen_t len;12 struct sockaddr *preply_addr;13 preply_addr = Malloc(servlen);14 Setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));15 FD_ZERO(&rset);16 Sigemptyset(&sigset_empty);17 Sigemptyset(&sigset_alrm);18 Sigaddset(&sigset_alrm, SIGALRM);19 Signal(SIGALRM, recvfrom_alarm);20 while (Fgets(sendline, MAXLINE, fp) != NULL) {21 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);22 Sigprocmask(SIG_BLOCK, &sigset_alrm, NULL);23 alarm(5);24 for (;;) {25 FD_SET(sockfd, &rset);26 n = pselect(sockfd + 1, &rset, NULL, NULL, NULL, &sigset_empty);27 if (n < 0) {28 if (errno == EINTR)29 break;30 else31 err_sys("pselect error");32 } else if (n != 1)33 err_sys("pselect error; returned %d", n);34 len = servlen;35 n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);36 recvline[n] = 0; /* завершающий нуль */37 printf("from %s: %s",38 Sock_ntop_host(preply_addr, len), recvline);39 }40 }41 free(preply_addr);42 }43 static void44 recvfrom_alarm(int signo)45 {46 return; /* просто прерываем recvfrom() */47 }Мы блокируем сигнал22-23и вызываем функциюSIGALRM. Последний аргумент этой функции — указатель на нашу переменнуюpselect, являющуюся набором сигналов, в котором нет блокированных сигналов (все сигналы разблокированы). Функцияsigset_emptyсохранит текущую маску сигналов (которая блокируетpselect), проверит заданные дескрипторы, заблокируется при необходимости с маской сигналов, установленной в пустой набор, но перед завершением функции маска сигналов процесса будет переустановлена в исходное значение, которое она имела при вызове функцииSIGALRM. Ключ к пониманию функцииpselectв том, что установка маски сигналов, проверка дескрипторов и переустановка маски сигнала — это атомарные операции по отношению к вызывающему процессу.pselectЕсли наш сокет готов для чтения, мы вызываем функцию34-38, зная, что она не заблокируется.recvfromКак мы упоминали в разделе 6.9, функция
— относительно новая среди других, описываемых спецификацией POSIX. Из всех систем, показанных на рис. 1.7, эту функцию поддерживают только FreeBSD и Linux. Тем не менее в листинге 20.4 представлена простая, хотя и некорректная ее реализация. Мы приводим ее здесь, несмотря на некорректность, чтобы продемонстрировать три стадии решения: установку маски сигнала в значение, заданное вызывающей функцией, с сохранением текущей маски, проверку дескрипторов и переустановку маски сигнала.pselect