Блокирование и разблокирование сигнала с помощью функции 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 }

Данный текст является ознакомительным фрагментом.