Один из способов устройства сервера, который мы не рассматриваем в этой главе, — это сервер, инициируемый демоном inetd (см. раздел 13.5). С точки зрения управления процессами такой сервер подразумевает использование функций fork и exec, так что затраты времени центрального процессора будут еще больше, чем показанные в строке 1 для параллельного сервера.
30.6. Сервер TCP с предварительным порождением процессов без блокировки для вызова accept
В первом из рассматриваемых нами «усовершенствованных» серверов используется технология, называемая fork
каждый раз при поступлении очередного запроса от клиента, сервер создает при запуске некоторое количество дочерних процессов, и впоследствии они обслуживают клиентские запросы по мере установления соединений с клиентами. На рис. 30.1 показан сценарий, при котором родитель предварительно создал
Рис. 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;
4 int
5 main(int argc, char **argv)
6 {
7 int listenfd, i;
8 socklen_t addrlen;
9 void sig_int(int);
10 pid_t child_make(int, int, int);
11 if (argc == 3)
12 listenfd = Tcp_listen(NULL, argv[1], &addrlen);
13 else if (argc == 4)
14 listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
15 else
16 err_quit("usage: serv02 [
17 nchildren = atoi(argv[argc - 1]);
18 pids = Calloc(nchildren, sizeof(pid_t));
19 for (i = 0; i < nchildren; i++)
20 pids[i] = child_make(i, listenfd, addrlen); /* возвращение родительского процесса */
21 Signal (SIGINT, sig_int);
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии