В листинге 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 [ <host> ] <port#>");

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: вывод полного времени центрального процессора

//server/pr_cpu_time.c

 1 #include "unp.h"

 2 #include <sys/resource.h>

 3 #ifndef HAVE_GETRUSAGE_PROTO

 4 int getrusage(int, struct rusage*);

 5 #endif

 6 void

 7 pr_cpu_time(void)

 8 {

 9  double user, sys;

10  struct rusage myusage, childusage;

11  if (getrusage(RUSAGE_SELF, &myusage) < 0)

12   err_sys("getrusage error");

13  if (getrusage(RUSAGE_CHILDREN, &childusage) < 0)

14   err_sys("getrusage error");

15  user = (double)myusage.ru_utime.tv_sec +

16   myusage.ru_utime.tv_usec / 1000000.0;

17  user += (double)childusage.ru_utime.tv_sec +

18   childusage.ru_utime.tv_usec / 1000000.0;

19  sys = (double)myusage.ru_stime.tv_sec +

20   myusage.ru_stime.tv_usec / 1000000.0;

21  sys += (double)childusage.ru_stime.tv_sec +

22   childusage.ru_stime.tv_usec / 1000000.0;

21  printf("\nuser time = %g, sys time = %g\n", user, sys);

22 }

Функция getrusage вызывается дважды: она позволяет получить данные об использовании ресурсов вызывающим процессом (RUSAGE_SELF) и всеми его дочерними процессами, которые завершили свое выполнение (RUSAGE_CHILDREN). Выводится время, затраченное центральным процессором на выполнение пользовательского процесса (общее пользовательское время, total user time), и время, которое центральный процессор затратил внутри ядра на выполнение задач, заданных вызывающим процессом (общее системное время, total system time).

Возвращаясь к листингу 30.2, мы видим, что для обработки каждого клиентского запроса вызывается функция web_child. Эта функция показана в листинге 30.5.


Перейти на страницу:
Изменить размер шрифта: