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

Традиционно параллельный сервер TCP вызывает функцию fork для порождения нового дочернего процесса, который будет выполнять обработку очередного клиентского запроса. Это позволяет серверу обрабатывать несколько запросов одновременно, выделяя по одному дочернему процессу для каждого клиента. Единственным ограничением на количество одновременно обрабатываемых клиентских запросов является ограничение операционной системы на количество дочерних процессов, допустимое для пользователя, в сеансе которого работает сервер. Листинг 5.9 содержит пример параллельного сервера, и большинство серверов TCP написаны в том же стиле.

Проблема с параллельными серверами заключается в количестве времени, которое тратит центральный процессор на выполнение функции fork для порождения нового дочернего процесса для каждого клиента. Давным-давно, в конце 80-х годов XX века, когда наиболее загруженные серверы обрабатывали сотни или тысячи клиентов за день, это было приемлемо. Но расширение Сети изменило требования. Теперь загруженными считаются серверы, обрабатывающие миллионы соединений TCP в день. Сказанное относится лишь к одиночным узлам, но наиболее загруженные сайты используют несколько узлов, распределяя нагрузку между ними (в разделе 14.2 [112] рассказывается об общепринятом способе распределения этой нагрузки, называемом циклическим обслуживанием DNS — DNS round robin). В последующих разделах описаны различные способы, позволяющие избежать вызова функции fork для каждого клиентского запроса, но тем не менее параллельные серверы остаются широко распространенными.

В листинге 30.2 показана функция main для нашего параллельного сервера TCP.

Листинг 30.2. Функция main для параллельного сервера TCP

//server/serv01.c

 1 include "unp.h"

 2 int

 3 main(int argc, char **argv)

 4 {

 5  int listenfd, connfd;

 6  pid_t childpid;

 7  void sig_chld(int), sig_int(int), web_child(int);

 8  socklen_t clilen, addrlen;

 9  struct sockaddr *cliaddr;

10  if (argc == 2)

11   listenfd = Tcp_listen(NULL, argv[1], &addrlen);

12  else if (argc == 3)

13   listenfd = Tcp_listen(argv[1], argv[2], &addrlen);

14  else

15   err_quit("usage: serv01 [ ] ");

16  cliaddr = Malloc(addrlen);

17  Signal(SIGCHLD, sig_chld);

18  Signal(SIGINT, sig_int);

19  for (;;) {

20   clilen = addrlen;

21   if ((connfd = accept(listenfd, cliaddr, &clilen)) < 0) {

22    if (errno == EINTR)

23     continue; /* назад к for() */

24    else

25     err_sys("accept error");

26   }

27   if ((childpid = Fork()) == 0) { /* дочерний процесс */

28    Close(listenfd); /* закрываем прослушиваемый сокет */

29    web_child(connfd); /* обрабатываем запрос */

30    exit(0);

31   }

32   Close(connfd); /* родительский процесс закрывает

                       присоединенный сокет */

33  }

34 }

Эта функция аналогична функции, показанной в листинге 5.9: она вызывает функцию fork для каждого клиентского соединения и обрабатывает сигналы SIGCHLD, приходящие от закончивших свое выполнение дочерних процессов. Тем не менее мы сделали эту функцию не зависящей от протокола за счет вызова функции tcp_listen. Мы не показываем обработчик сигнала sig_chld: он совпадает с показанным в листинге 5.8, но только без функции printf.

Мы также перехватываем сигнал SIGINT, который генерируется при вводе символа прерывания. Мы вводим этот символ после завершения работы клиента, чтобы было выведено время, потраченное центральным процессором на выполнение данной программы. В листинге 30.3 показан обработчик сигнала. Это пример обработчика сигнала, который никогда не возвращает управление.

Листинг 30.3. Обработчик сигнала SIGINT

//server/serv01.c

35 void

36 sig_int(int signo)

37 {

38  void pr_cpu_time(void);

39  pr_cpu_time();

40  exit(0);

41 }

В листинге 30.4 показана функция pr_cpu_time, вызываемая из обработчика сигнала.

Листинг 30.4. Функция pr_cpu_time: вывод полного времени центрального процессора

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

Все книги серии Мастер-класс

Секреты резьбы по дереву
Секреты резьбы по дереву

Изделия из древесины и материалов, имитирующих ее текстуру, привычным образом окружают нас в повседневной жизни, поэтому мы относимся к ней как к чему-то обыденному. Но как только ее коснется умелая рука мастера резьбы по дереву, рождается произведение искусства и раскрываются такие качества древесины, как богатая фактура, разнообразие цветов, особая теплота. Эта книга поможет читателю открыть для себя удивительный мир творчества и познать секреты резьбы по дереву. Автор надеется, что начинающие резчики найдут в ней интересный и полезный материал, который позволит им стать мастерами. В приложении представлены рисунки орнаментов и различных узоров, которые на первых порах можно копировать, а по мере приобретения навыка на их основе разрабатывать свои образцы.

Галина Алексеевна Серикова

Сделай сам / Хобби и ремесла / Руководства / Дом и досуг / Словари и Энциклопедии

Похожие книги