Чтение онлайн

на главную - закладки

Жанры

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

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.

Листинг 30.5. Функция web_child: обработка каждого клиентского запроса

//server/web_child.c

1 #include "unp.h"

2 #define MAXN 16384 /* максимальное количество байтов, которое клиент

может запросить */

3 void

4 web_child(int sockfd)

5 {

6 int ntowrite;

7 ssize_t nread;

8 char line[MAXLINE], result[MAXN];

9 for (;;) {

10 if ((nread = Readline(sockfd, line, MAXLINE)) == 0)

11 return; /* соединение закрыто другим концом */

12 /* line задает, сколько байтов следует отправлять обратно */

13 ntowrite = atol(line);

14 if ((ntowrite <= 0) || (ntowrite > MAXN))

15 err_quit("client request for bytes", ntowrite);

16 Writen(sockfd, result, ntowrite);

17 }

18 }

Установив соединение с сервером, клиент записывает одну строку, задающую количество байтов, которое сервер должен вернуть. Это отчасти похоже на HTTP: клиент отправляет небольшой запрос, а сервер в ответ отправляет требуемую информацию (часто это файл HTML или изображение GIF). В случае HTTP сервер обычно закрывает соединение после отправки клиенту затребованных данных, хотя более новые версии используют постоянные соединения( persistent connection), оставляя соединения TCP открытыми для дополнительных клиентских запросов. В нашей функции

web_child
сервер допускает дополнительные запросы от клиента, но, как мы видели в листинге 24.1, клиент посылает серверу только по одному запросу на каждое соединение, а по получении ответа от сервера это соединение закрывается.

В строке 1 табл. 30.1 показаны результаты измерения времени, затраченного параллельным сервером. При сравнении со следующими строками этой таблицы видно, что параллельный сервер тратит больше процессорного времени, чем все другие

типы серверов — то, что мы и ожидали при вызове функции
fork
.

ПРИМЕЧАНИЕ

Один из способов устройства сервера, который мы не рассматриваем в этой главе, — это сервер, инициируемый демоном inetd (см. раздел 13.5). С точки зрения управления процессами такой сервер подразумевает использование функций fork и exec, так что затраты времени центрального процессора будут еще больше, чем показанные в строке 1 для параллельного сервера.

30.6. Сервер TCP с предварительным порождением процессов без блокировки для вызова accept

В первом из рассматриваемых нами «усовершенствованных» серверов используется технология, называемая предварительным созданием процессов( preforking). Вместо того чтобы вызывать функцию

fork
каждый раз при поступлении очередного запроса от клиента, сервер создает при запуске некоторое количество дочерних процессов, и впоследствии они обслуживают клиентские запросы по мере установления соединений с клиентами. На рис. 30.1 показан сценарий, при котором родитель предварительно создал Nдочерних процессов, и в настоящий момент имеется два соединения с клиентами.

Рис. 30.1. Предварительное создание дочерних процессов сервером

Преимущество этой технологии заключается в том, что обслуживание нового клиента не требует вызова функции

fork
родительским процессом, тем самым стоимость этого обслуживания понижается. Недостатком же является необходимость угадать, сколько дочерних процессов нужно создать при запуске. Если в некоторый момент времени количество имеющихся дочерних процессов будет равно количеству обслуживаемых клиентов, то дополнительные клиентские запросы будут игнорироваться до того момента, когда освободится какой-либо дочерний процесс. Но, как сказано в разделе 4.5, клиентские запросы в такой ситуации игнорируются не полностью. Для каждого из этих дополнительных клиентов ядро выполнит трехэтапное рукопожатие (при этом общее количество соединений не может превышать значения аргумента
backlog
функции
listen
), и при вызове функции
accept
установленные соединения будут переданы серверу. При этом, однако, приложение-клиент может заметить некоторое ухудшение в скорости получения ответа, так как, хотя функция
connect
может быть выполнена сразу же, запрос может не поступать на обработку еще некоторое время.

За счет некоторого дополнительного усложнения кода всегда можно добиться того, что сервер справится со всеми клиентскими запросами. От родительского процесса требуется постоянно отслеживать количество свободных дочерних процессов, и если это количество падает ниже некоторого минимального предела, родитель должен вызвать функцию

fork
и создать недостающее количество дочерних процессов. Аналогично, если количество свободных дочерних процессов превосходит некоторую максимальную величину, некоторые из этих процессов могут быть завершены родителем, так как излишнее количество свободных дочерних процессов тоже отрицательно влияет на производительность (об этом мы поговорим чуть позже).

Но прежде чем углубляться в детали, исследуем основную структуру этого типа сервера. В листинге 30.6 показана функция

main
для первой версии нашего сервера с предварительным порождением дочерних процессов.

Листинг 30.6. Функция main сервера с предварительным порождением дочерних процессов

//server/serv02.c

1 #include "unp.h"

2 static int nchildren;

3 static pid_t *pids;

Поделиться:
Популярные книги

Решала

Иванов Дмитрий
10. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Решала

Все не случайно

Юнина Наталья
Любовные романы:
современные любовные романы
7.10
рейтинг книги
Все не случайно

Око василиска

Кас Маркус
2. Артефактор
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Око василиска

Неудержимый. Книга XIV

Боярский Андрей
14. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XIV

Жребий некроманта 3

Решетов Евгений Валерьевич
3. Жребий некроманта
Фантастика:
боевая фантастика
5.56
рейтинг книги
Жребий некроманта 3

Делегат

Астахов Евгений Евгеньевич
6. Сопряжение
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Делегат

Запасная дочь

Зика Натаэль
Фантастика:
фэнтези
6.40
рейтинг книги
Запасная дочь

Real-Rpg. Еретик

Жгулёв Пётр Николаевич
2. Real-Rpg
Фантастика:
фэнтези
8.19
рейтинг книги
Real-Rpg. Еретик

Восход. Солнцев. Книга VI

Скабер Артемий
6. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга VI

Действуй, дядя Доктор!

Юнина Наталья
Любовные романы:
короткие любовные романы
6.83
рейтинг книги
Действуй, дядя Доктор!

Авиатор: назад в СССР 12

Дорин Михаил
12. Покоряя небо
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Авиатор: назад в СССР 12

Адепт. Том 1. Обучение

Бубела Олег Николаевич
6. Совсем не герой
Фантастика:
фэнтези
9.27
рейтинг книги
Адепт. Том 1. Обучение

Земная жена на экспорт

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.57
рейтинг книги
Земная жена на экспорт

Варлорд

Астахов Евгений Евгеньевич
3. Сопряжение
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Варлорд