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
28 if (errno == EINTR)
29 break;
30 else
31 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 void
44 recvfrom_alarm(int signo)
45 {
46 return; /* просто прерываем recvfrom() */
47 }
22-23
Мы блокируем сигнал SIGALRM
и вызываем функцию pselect
. Последний аргумент этой функции — указатель на нашу переменную sigset_empty
, являющуюся набором сигналов, в котором нет блокированных сигналов (все сигналы разблокированы). Функция pselect
сохранит текущую маску сигналов (которая блокирует SIGALRM
), проверит заданные дескрипторы, заблокируется при необходимости с маской сигналов, установленной в пустой набор, но перед завершением функции маска сигналов процесса будет переустановлена в исходное значение, которое она имела при вызове функции pselect
. Ключ к пониманию функции pselect
в том, что установка маски сигналов, проверка дескрипторов и переустановка маски сигнала — это атомарные операции по отношению к вызывающему процессу.
34-38
Если наш сокет готов для чтения, мы вызываем функцию recvfrom
, зная, что она не заблокируется.
Как мы упоминали в разделе 6.9, функция pselect
— относительно новая среди других, описываемых спецификацией POSIX. Из всех систем, показанных на рис. 1.7, эту функцию поддерживают только FreeBSD и Linux. Тем не менее в листинге 20.4 представлена простая, хотя и некорректная ее реализация. Мы приводим ее здесь, несмотря на некорректность, чтобы продемонстрировать три стадии решения: установку маски сигнала в значение, заданное вызывающей функцией, с сохранением текущей маски, проверку дескрипторов и переустановку маски сигнала.
Листинг 20.4. Простая некорректная реализация функции pselect
//lib/pselect.c
9 #include "unp.h"
10 int
11 pselect(int nfds, fd_set *rset, fd_set *wset, fd_set *xset,
12 const struct timespec *ts, const sigset_t *sigmask)
13 {
14 int n;
15 struct timeval tv;
16 sigset_t savemask;
17 if (ts != NULL) {
18 tv.tv_sec = ts->tv_sec;
19 tv.tv_usec = ts->tv_nsec / 1000; /* наносекунды -> микросекунды */
20 }
21 sigprocmask(SIG_SETMASK, sigmask, &savemask); /* маска вызывающего
процесса */
22 n = select(nfds, rset, wset, xset., (ts == NULL) ? NULL : &tv);
23 sigprocmask(SIG_SETMASK, &savemask, NULL); /* восстанавливаем
исходную маску */
24 return (n);
25 }
Использование функций sigsetjmp и siglongjmp
Нашу проблему можно решить корректно, если отказаться от прерывания блокированного системного вызова обработчиком сигнала, вместо этого вызвав из обработчика сигнала функцию siglongjmp
. Этот метод называется нелокальным оператором goto ( nonlocal goto ), поскольку мы можем использовать его для перехода из одной функции в другую. В листинге 20.5 проиллюстрирована эта технология.
Листинг 20.5. Вызов функций sigsetjmp и siglongjmp из обработчика сигнала
//bcast/dgclibcast5.c
1 #include "unp.h"
2 #include
3 static void recvfrom_alarm(int);
4 static sigjmp_buf jmpbuf;
5 void
6 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
7 {
8 int n;
9 const int on = 1;
10 char sendline[MAXLINE], recvline[MAXLINE + 1];
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 Signal(SIGALRM, recvfrom_alarm);
16 while (Fgets(sendline, MAXLINE, fp) != NULL) {
17 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
18 alarm(5);
19 for (;;) {
20 if (sigsetjmp(jmpbuf, 1) != 0)
21 break;
22 len = servlen;
23 n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);
24 recvline[n] = 0; /* null terminate */
25 printf("from %s: %s",
26 Sock_ntop_host(preply_addr, len), recvline);
27 }
28 }
29 free(preply_addr);
30 }
31 static void
32 recvfrom_alarm(int signo)
33 {
34 siglongjmp(jmpbuf, 1);
35 }
Размещение буфера перехода в памяти
4
Мы выделяем буфер перехода, который будет использовать наша функция и ее обработчик сигнала.
Вызов функции sigsetjmp
20-23
Когда мы вызываем функцию sigsetjmp
непосредственно из нашей функции dg_cli
, она устанавливает буфер перехода и возвращает нуль. Мы продолжаем работать дальше и вызываем функцию recvfrom
.
Читать дальше
Конец ознакомительного отрывка
Купить книгу