Книга: UNIX: разработка сетевых приложений

Блокирование и разблокирование сигнала с помощью функции pselect

Блокирование и разблокирование сигнала с помощью функции pselect

Одним из корректных решений будет использование функции pselect (см. раздел 6.9), как показано в листинге 20.3.

Листинг 20.3. Блокирование и разблокирование сигналов с помощью функции pselect

//bcast/dgclibcast4.с
 1 #include "unp.h"
 2 static void recvfrom_alarm(int);
 3 void
 4 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     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 }

Оглавление книги


Генерация: 1.111. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз