30.13. Резюме

30.13. Резюме

В этой главе мы рассмотрели 9 различных версий сервера и их работу с одним и тем же веб-клиентом, чтобы сравнить значения времени центрального процессора, затраченного на управление процессом.

0. Последовательный сервер (точка отсчета — управление процессом отсутствует).

1. Параллельный сервер, по одному вызову функции fork для каждого клиента.

2. Предварительное порождение дочерних процессов, каждый из которых вызывает функцию accept.

3. Предварительное порождение дочерних процессов с блокировкой файла для защиты функции accept.

4. Предварительное порождение дочерних процессов с блокировкой взаимного исключения дочерними процессами для защиты функции accept.

5. Предварительное порождение дочерних процессов с передачей дескриптора от родительского процесса дочернему.

6. Параллельный сервер, поочередное создание потоков по мере поступления клиентских запросов.

7. Предварительное порождение потоков с блокировкой взаимного исключения потоками для защиты функции accept.

8. Предварительное порождение потоков, основной поток вызывает функцию accept.

Резюмируя материал этой главы, можно сделать несколько комментариев.

? Если сервер не слишком загружен, хорошо работает традиционная модель параллельного сервера, в которой при поступлении очередного клиентского запроса вызывается функция fork для создания нового дочернего процесса. Этот вариант допускает комбинирование с демоном inetd, принимающим все клиентские запросы. Остальные версии применимы в случае загруженных серверов, таких как веб-серверы.

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

? Некоторые реализации допускают блокирование нескольких потоков или дочерних процессов в вызове функции accept, в то время как другие реализации требуют использования блокировки того или иного типа для защиты accept. Можно использовать для этого либо блокировку файла, либо блокировку взаимного исключения Pthreads.

? Как правило, версия, в которой каждый поток или дочерний процесс вызывает функцию accept, проще и быстрее, чем версия, где вызов функции accept осуществляется только основным потоком (или родительским процессом), впоследствии передающим дескриптор присоединенного сокета другому потоку или дочернему процессу.

? Блокировка всех дочерних процессов или программных потоков в вызове функции accept предпочтительнее, чем блокировка в вызове функции select, что объясняется возможностью появления коллизий при вызове функции select.

? Использование потоков, как правило, дает больший выигрыш во времени, чем использование процессов. Но выбор между версиями 1 и 6 (один дочерний процесс на каждый запрос и один поток на каждый запрос) зависит от свойств операционной системы и от того, какие еще программы задействованы в обслуживании клиентских запросов. Например, если сервер, принимающий клиентское соединение, вызывает функции fork и exec, то может оказаться быстрее породить с помощью функции fork процесс с одним потоком, чем процесс с несколькими потоками.

Данный текст является ознакомительным фрагментом.