Причина возникновения этого множителя теряется в истории [57]. Но если мы рассматриваем backlog как способ задания максимального числа установленных соединений, которые ядро помещает в очередь прослушиваемого сокета (об этом вскоре будет рассказано), этот множитель нужен для учета не полностью установленных соединений, находящихся в очереди [8].
Не следует задавать нулевое значение аргументу
backlog
, поскольку различные реализации интерпретируют это по-разному (см. табл. 4.6). Некоторые реализации допускают помещение в очередь одного соединения, в то время как в других вообще невозможно помещать соединения в очередь. Если вы не хотите, чтобы клиенты соединялись с вашим прослушиваемым сокетом, просто закройте прослушиваемый сокет.
Если трехэтапное рукопожатие завершается нормально (то есть без потерянных сегментов и повторных передач), запись остается в очереди не полностью установленных соединений на время одного периода обращения (round-trip time, RTT), какое бы значение ни имел этот параметр для конкретного соединения между клиентом и сервером. В разделе 14.4 [112] показано, что для одного веб-сервера средний период RTT оказался равен 187 мс. (Чтобы редкие большие числа не искажали картину, здесь использована медиана, а не обычное среднее арифметическое по всем клиентам.)
Традиционно в примерах кода всегда используется значение
backlog
, равное 5, поскольку это было максимальное значение, которое поддерживалось в системе 4.2BSD. Это было актуально в 80-х, когда загруженные серверы могли обрабатывать только несколько сотен соединений в день. Но с ростом Сети (WWW), когда серверы обрабатывают миллионы соединений в день, столь малое число стало абсолютно неприемлемым [112, с. 187–192]. Серверам HTTP необходимо намного большее значение аргумента
backlog
, и новые ядра должны поддерживать такие значения.
В настоящее время многие системы позволяют администраторам изменять максимальное значение аргумента backlog.
Возникает вопрос: какое значение аргумента
backlog
должно задавать приложение, если значение 5 часто является неадекватным? На этот вопрос нет простого ответа. Серверы HTTP сейчас задают большее значение, но если заданное значение является в исходном коде константой, то для увеличения константы требуется перекомпиляция сервера. Другой способ — принять некоторое значение по умолчанию и предоставить возможность изменять его с помощью параметра командной строки или переменной окружения. Всегда можно задавать значение больше того, которое поддерживается ядром, так как ядро должно обрезать значение до максимального, не возвращая при этом ошибку [128, с. 456].
Мы приводим простое решение этой проблемы, изменив нашу функцию-обертку для функции
listen
. В листинге 4.1
[1]представлен действующий код. Переменная окружения
LISTENQ
позволяет переопределить значение по умолчанию.
Листинг 4.1. Функция-обертка для функции listen, позволяющая переменной окружения переопределить аргумент backlog
//lib/wrapsock.c
137 void
138 Listen(int fd, int backlog)
139 {
140 char *ptr;
141 /* может заменить второй аргумент на переменную окружения */
142 if ((ptr = getenv("LISTENQ")) != NULL)
143 backlog = atoi(ptr);
144 if (listen(fd, backlog) < 0)
145 err_sys("listen error");
146 }
Традиционно в руководствах и книгах утверждалось, что помещение фиксированного числа соединений в очередь позволяет обрабатывать случай загруженного серверного процесса между последовательными вызовами функции
accept
. При этом подразумевается, что из двух очередей больше записей будет содержаться, вероятнее всего, в очереди полностью установленных соединений. Но оказалось, что для действительно загруженных веб-серверов это не так. Причина задания большего значения
backlog
в том, что очередь не полностью установленных соединений растет по мере поступления сегментов SYN от клиентов; элементы очереди находятся в состоянии ожидания завершения трехэтапного рукопожатия.