books-read.com
books-read.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

Наш ресурс дает возможность бесплатно читать книгу онлайн Уильям Стивенс - UNIX: разработка сетевых приложений. Жанр: Программное обеспечение издательство -, год -. Сайт books-read.com дает возможность читать полную версию книги без регистрации и sms. Все книги онлайн, не надо качать fb2, epub, txt.
Добавить книгу Уильям Стивенс - UNIX: разработка сетевых приложений в приложение ЧИТАТЬ КНИГУ ОФЛАЙН в приложении ios/android
Перейти на страницу:

Листинг 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 }

Использование функций sigsetjmp и siglongjmp

Нашу проблему можно решить корректно, если отказаться от прерывания блокированного системного вызова обработчиком сигнала, вместо этого вызвав из обработчика сигнала функцию siglongjmp. Этот метод называется нелокальным оператором goto (nonlocal goto), поскольку мы можем использовать его для перехода из одной функции в другую. В листинге 20.5 проиллюстрирована эта технология.

Листинг 20.5. Вызов функций sigsetjmp и siglongjmp из обработчика сигнала

//bcast/dgclibcast5.c

 1 #include "unp.h"

 2 #include <setjmp.h>


 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.

Обработка сигнала SIGALRM и вызов функции siglongjmp

31-35 Когда сигнал доставлен, мы вызываем функцию siglongjmp. Это заставляет sigsetjmp в функции dg_cli возвратить значение, равное второму аргументу (1), который должен быть ненулевым. Это приведет к завершению цикла for в функции dg_cli.

Использование функций sigsetjmp и siglongjmp подобным образом гарантирует, что мы не останемся навсегда блокированы в вызове функции recvfrom из-за доставки сигнала в неподходящее время. Однако такое решение создает иную потенциальную проблему. Если сигнал доставляется в тот момент, когда функция printf осуществляет вывод данных, управление будет передано из printf обратно на sigsetjmp. При этом в структурах данных printf могут возникнуть противоречия. Чтобы предотвратить эту проблему, следует объединить блокирование и разблокирование сигналов, показанное в листинге 20.2, с помощью нелокального оператора goto.

Применение IPC в обработчике сигнала функции

Существует еще один корректный путь решения нашей проблемы. Вместо того чтобы просто возвращать управление и, как мы надеемся, прерывать блокированную функцию recvfrom, наш обработчик сигнала при помощи средств IPC (Interprocess Communications — взаимодействие процессов) может сообщить функции dg_cli о том, что время таймера истекло. Это аналогично предложению, сделанному нами раньше, когда обработчик сигнала устанавливал глобальную переменную had_alarm по истечении времени таймера. Глобальная переменная использовалась как некая разновидность IPC (поскольку она была доступна и нашей функции, и обработчику сигнала). Однако при таком решении наша функция должна была проверять эту переменную, что могло привести к проблемам синхронизации в том случае, когда сигнал доставлялся приблизительно в это же время.

Листинг 20.6 демонстрирует использование канала внутри процесса. Обработчик сигналов записывает в канал 1 байт, когда истекает время таймера, а наша функция dg_cli считывает этот байт, чтобы определить, когда завершить свой цикл for. Что замечательно в этом решении — проверка готовности канала осуществляется функцией select. С ее помощью мы проверяем, готов ли к считыванию сокет или канал.

Листинг 20.6. Использование канала в качестве IPC между обработчиком сигнала и нашей функцией

Перейти на страницу:

Уильям Стивенс читать все книги автора по порядку

Уильям Стивенс - на сайте онлайн книг books-read.com Вы можете читать полные версии книг автора в одном месте.


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

Отзывы читателей о книге UNIX: разработка сетевых приложений, автор: Уильям Стивенс. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор books-read.com


Прокомментировать
Подтвердите что вы не робот:*