Распределение клиентских соединений между дочерними процессами
Используя функцию, показанную в листинге 30.10, мы можем исследовать распределение клиентских запросов между свободными дочерними процессами. Результат показан в табл. 30.2. Операционная система распределяет блокировки файла равномерно между ожидающими процессами, и такое поведение характерно для нескольких протестированных нами систем.
30.8. Сервер TCP с предварительным порождением процессов и защитой вызова accept при помощи взаимного исключения
Как мы уже говорили,
существует несколько способов синхронизации процессов путем блокирования. Блокировка файла по стандарту POSIX, рассмотренная в предыдущем разделе, переносится на все POSIX-совместимые системы, но она подразумевает некоторые операции с файловой системой, которые могут потребовать времени. В этом разделе мы будем использовать блокировку при помощи взаимного исключения, обладающую тем преимуществом, что ее можно применять для синхронизации не только потоков внутри одного процесса, но и потоков, относящихся к различным процессам.
Функция
main
остается такой же, как и в предыдущем разделе, то же относится к функциям
child_make
и
child_main
. Меняются только три функции, осуществляющие блокировку. Чтобы использовать взаимное исключение между различными процессами, во-первых, требуется хранить это взаимное исключение в разделяемой процессами области памяти, а во-вторых, библиотека потоков должна получить указание о том, что взаимное исключение совместно используется различными процессами.
ПРИМЕЧАНИЕ
Требуется также, чтобы библиотека потоков поддерживала атрибут PTHREAD_PROCESS_SHARED.
Существует несколько способов разделения памяти между различными процессами, что мы подробно описываем во втором томе [2] данной серии. В этом примере мы используем функцию
mmap
с устройством
/dev/zero
, которое работает с ядрами Solaris и другими ядрами SVR4. В листинге 30.14 показана только функция
my_lock_init
.
2
Стивенс У. UNIX: взаимодействие процессов. — СПб.: Питер, 2002.
Листинг 30.14. Функция my_lock_init: использование взаимного исключения потоками, относящимися к различным процессам (технология Pthread)
//server/lock_pthread.c
1 #include "unpthread.h"
2 #include <sys/mman.h>
3 static pthread_mutex_t *mptr; /* фактически взаимное исключение будет
. Количество байтов (второй аргумент этой функции) — это размер переменной
pthread_mutex_t
. Затем дескриптор закрывается, но для нас это не имеет значения, так как файл уже отображен в память.
13-15
В приведенных ранее примерах взаимных исключений Pthread мы инициализировали глобальные статические взаимные исключения, используя константу
PTHREAD_MUTEX_INITIALIZER
(см., например, листинг 26.12). Но располагая взаимное исключение в совместно используемой памяти, мы должны вызвать некоторые библиотечные функции Pthreads, чтобы сообщить библиотеке о наличии семафора в совместно используемой памяти и о том, что он будет применяться для синхронизации потоков, относящихся к различным процессам. Мы должны инициализировать структуру
pthread_mutexattr_t
задаваемыми по умолчанию атрибутами взаимного исключения, а затем установить значение атрибута
PTHREAD_PROCESS_SHARED
. (По умолчанию значением этого атрибута должно быть
PTHREAD_PROCESS_PRIVATE
, что подразумевает использование взаимного исключения только в пределах одного процесса.) Затем вызов
. Они содержат вызовы функций Pthreads, предназначенных для блокирования и разблокирования взаимного исключения.
Листинг 30.15. Функции my_lock_wait и my_lock_release: использование блокировок Pthread
//server/lock_pthread.c
17 void
18 my_lock_wait
19 {
20 Pthread_mutex_lock(mptr),
21 }
22 void
23 my_lock_release
24 {
25 Pthread_mutex_unlock(mptr);
26 }
Сравнивая строки 3 и 4 табл. 30.1, можно заметить, что версия, использующая синхронизацию процессов при помощи взаимного исключения, характеризуется более высоким быстродействием, чем версия с блокировкой файла.
30.9. Сервер TCP с предварительным порождением процессов: передача дескриптора