Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:

Рис. 25.2. Структуры данных, используемые для хранения прибывающих дейтаграмм и структур адресов их сокетов
13-15
Переменная iget
является индексом следующего элемента массива для обработки в основном цикле, а переменная iput
— это индекс следующего элемента массива, в котором сохраняется результат действия обработчика сигнала. Переменная nqueue
обозначает полное количество дейтаграмм, предназначенных для обработки в основном цикле.
В листинге 25.2 показан основной цикл сервера — функция dg_echo
.
Листинг 25.2. Функция dg_echo: основной обрабатывающий цикл сервера
//sigio/dgecho01.c
19 void
20 dg_echo(int sockfd_arg, SA *pcliaddr, socklen_t clilen_arg)
21 {
22 int i;
23 const int on = 1;
24 sigset_t zeromask, newmask, oldmask;
25 sockfd = sockfd_arg;
26 clilen = clilen_arg;
27 for (i = 0; i < QSIZE; i++) { /* инициализация очереди */
28 dg[i].dg_data = Malloc(MAXDG);
29 dg[i].dg_sa = Malloc(clilen);
30 dg[i].dg_salen = clilen;
31 }
32 iget = iput = nqueue = 0;
33 Signal(SIGHUP, sig_hup);
34 Signal(SIGIO, sig_io);
35 Fcntl(sockfd, F_SETOWN, getpid());
36 Ioctl(sockfd, FIOASYNC, &on);
37 Ioctl(sockfd. FIONBIO, &on);
38 Sigemptyset(&zeromask); /* инициализация трех наборов сигналов */
39 Sigemptyset(&oldmask);
40 Sigemptyset(&newmask);
41 Sigaddset(&newmask, SIGIO); /* сигнал, который хотим блокировать*/
42 Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
43 for (;;) {
44 while (nqueue == 0)
45 sigsuspend(&zeromask); /* ждем дейтаграмму для обработки */
46 /* разблокирование SIGIO */
47 Sigprocmask(SIG_SETMASK, &oldmask, NULL);
48 Sendto(sockfd, dg[iget].dg_data, dg[iget].dg_len, 0,
49 dg[iget].dg_sa, dg[iget].dg_salen);
50 if (++iget >= QSIZE)
51 iget = 0;
52 /* блокировка SIGIO */
53 Sigprocmask(SIG_BLOCK, &newmask, &oldmask);
54 nqueue--;
55 }
56 }
27-32
Дескриптор сокета сохраняется в глобальной переменной, поскольку он необходим обработчику сигналов. Происходит инициализация очереди принятых дейтаграмм.
33-37
Для сигналов SIGHUP
(он используется для диагностических целей) и SIGIO
устанавливаются обработчики. С помощью функции fcntl
задается владелец сокета, а с помощью функции ioctl
устанавливаются флаги ввода-вывода, управляемого сигналом, и неблокируемого ввода-вывода.
Ранее отмечалось, что для разрешения ввода-вывода, управляемого сигналом, в POSIX применяется флаг O_ASYNC функции fcntl, но поскольку большинство систем пока его не поддерживают, мы используем функцию ioctl. Поскольку большинство систем не поддерживают флаг O_NONBLOCK для включения неблокируемого ввода-вывода, здесь также рассмотрен вариант использования функции ioctl.
38-41
Инициализируется три набора сигналов: zeromask
(никогда не изменяется), oldmask
(хранит старую маску сигнала, когда SIGIO
блокируется) и newmask
. Функция sigaddset
включает в набор newmask
бит, соответствующий SIGIO
.
42-45
Функция sigprocmask
сохраняет текущую маску сигналов процесса в oldmask
, а затем выполняет логическое сложение, сравнивая newmask
с текущей маской сигналов. Такие действия блокируют сигнал SIGIO
и возвращают текущую маску сигналов. Далее мы заходим в цикл for
и проверяем счетчик nqueue
. Пока этот счетчик равен нулю, ничего делать не нужно, и мы вызываем функцию sigsuspend
. Эта функция POSIX, сохранив в одной из локальных переменных текущую маску сигналов, присваивает текущей маске значение аргумента zeromask
. Так как zeromask
является пустым набором сигналов, то разрешается доставка любых сигналов. Как только перехватывается сигнал и завершается обработчик, функция sigsuspend
также завершается. (Это необычная функция, поскольку она всегда возвращает ошибку EINTR
.) Прежде чем завершиться, функция sigsuspend
всегда устанавливает такое значение маски сигналов, которое предшествовало ее вызову (в данном случае newmask
). Таким образом гарантируется, что, когда функция sigsuspend
возвращает значение, сигнал SIGIO
блокирован. Именно поэтому можно проверять счетчик nqueue
, поскольку известно, что пока он проверяется, сигнал SIGIO
не может быть доставлен.
А что произойдет, если сигнал SIGIO не будет блокирован во время проверки переменной nqueue, используемой совместно основным циклом и обработчиком сигналов? Может случиться так, что проверка nqueue покажет нулевое значение, а сразу после проверки возникнет сигнал и nqueue станет равна 1. Далее мы вызовем функцию sigsuspend и перейдем в режим ожидания, в результате чего пропустим сигнал. После вызова функции sigsuspend мы не выйдем из режима ожидания, пока не поступит другой сигнал. Это похоже на ситуацию гонок, описанную в разделе 20.5
46-51
Разблокируем сигнал SIGIO с помощью вызова sigprocmask
, чтобы вернуть маске сигналов процесса значение, сохраненное ранее ( oldmask
). В этом случае ответ посылается с помощью функции sendto
. Индекс iget
увеличился на 1, и если его значение совпадает с количеством элементов массива, он снова обнуляется. Массив используется как кольцевой буфер. Обратите внимание, что нет необходимости блокировать сигнал SIGIO
во время изменения переменной iget
, поскольку этот индекс используется только в основном цикле и никогда не изменяется обработчиком сигнала.
52-54
Сигнал SIGIO
блокируется, а значение переменной nqueue
уменьшается на 1. Во время изменения данной переменной необходимо заблокировать сигнал, поскольку она используется совместно основным циклом и обработчиком сигнала. Также необходимо, чтобы сигнал SIGIO
был заблокирован, когда в начале цикла происходит проверка переменной nqueue
.
Альтернативным способом является удаление обоих вызовов функции sigprocmask
, находящихся внутри цикла for
, что предотвращает разблокирование сигнала и его последующее блокирование. Однако проблема состоит в следующем: в такой ситуации весь цикл выполняется при блокированном сигнале, что уменьшает быстроту реагирования обработчика сигнала. При этом дейтаграммы не будут теряться (если, конечно, буфер приема сокета достаточно велик), но выдача сигнала процессу будет задерживаться на время блокирования сигнала. Одной из задач при создании приложений, производящих обработку сигналов, должна быть минимизация времени блокирования сигнала.
Интервал:
Закладка: