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

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:

solaris % clientfd1 /tmp/fd1

/etc/ntp.conf файл из двух строк

multicastclient 224.0.1.1

driftfile /etc/ntp.drift

В первых двух случаях мы указываем имя файла, приводящее к возврату сообщения об ошибке. В третий раз сервер передает клиенту дескриптор файла из двух строк, который благополучно выводится.

ПРИМЕЧАНИЕ

Существует проблема, связанная с передачей дескриптора через дверь. Чтобы она проявилась в нашем примере, достаточно добавить вызов printf к процедуре сервера сразу после успешного вызова open. Вы увидите, что значение

дескриптора каждый раз увеличивается на единицу. Проблема в том, что сервер не закрывает дескрипторы после передачи их клиенту. Сделать это, вообще говоря, нелегко. Логично было бы выполнять закрытие дескриптора после возврата из door_return, после успешной отправки дескриптора клиенту, но возврата из door_return не происходит! Если бы мы использовали sendmsg для передачи дескриптора через доменный сокет Unix или ioctl для передачи дескриптора через канал в SVR4, мы могли бы закрыть его после возврата из sendmsg или ioctl. Однако с дверьми все по-другому, поскольку возврата из функции door_return не происходит. Единственный способ обойти проблему заключается в том, что процедура сервера должна запоминать все открытые дескрипторы и закрывать их некоторое время спустя, что несколько запутывает код.

Эта проблема должна быть исправлена в Solaris 2.7 добавлением атрибута DOOR RELEASE. Отправитель устанавливает поле d_attributes равным DOOR DESCRIPTOR | DOOR_RELEASE, что говорит системе о необходимости закрывать дескриптор после передачи его клиенту.

15.9. Функция door server_create

В листинге 15.6 мы показали, что библиотека дверей автоматически создает новые потоки для обслуживания запросов клиентов по мере их поступления. Они создаются библиотекой как неприсоединенные потоки (detached threads) с размером стека потока по умолчанию, с отключенной возможностью отмены потока (thread cancellation) и с маской сигналов и классом планирования (scheduling class), унаследованными от потока, вызвавшего door_create. Если мы хотим изменить какой-либо из этих параметров или хотим самостоятельно работать с пулом потоков сервера, можно воспользоваться функцией door_server_create и указать нашу собственную процедуру создания сервера:

#include <door.h>

typedef void Door_create_proc(door_info_t *);

Door_create_proc *door_server_create(Door_create_proc *proc);

/* Возвращает указатель на предыдущую процедуру создания сервера */
 

Как и при объявлении door_create в разделе 15.3, мы используем оператор typedef для упрощения прототипа библиотечной функции. Наш новый тип данных определяет процедуру создания сервера как принимающую один аргумент (указатель на структуру типа door_info_t) и ничего не возвращающую (void). При вызове door_server_create аргументом является указатель на нашу процедуру создания сервера, а возвращается указатель на предыдущую процедуру создания сервера. 

Наша процедура создания сервера вызывается при возникновении необходимости создания нового потока для обслуживания запроса клиента. Информация о том, какой из процедур сервера требуется новый поток, передается в структуре door_info_t, адрес которой принимается процедурой создания сервера. Поле di_proc содержит адрес процедуры сервера, а поле di_data содержит указатель на аргументы, передаваемые процедуре сервера при вызове.

Проще всего изучить происходящее на примере. Программа-клиент не претерпевает никаких изменений по сравнению с листингом 15.1. В программу-сервер добавляются две новые функции помимо процедуры сервера и функции main. На рис. 15.5 приведена схема сервера с четырьмя функциями и последовательностью их регистрации и вызова.

Рис. 15.5. Четыре функции в процессе-сервере

В

листинге 15.17 приведен текст функции main сервера.

Листинг 15.17. Функция main для примера с управлением пулом потоков

//doors/server6.c

42 int

43 main(int argc, char **argv)

44 {

45 if (argc != 2)

46 err_quit("usage: server6 <server-pathname>");

47 Door_server_create(my_create);

48 /* создание дескриптора двери и связывание его с именем */

49 Pthread_mutex_lock(&fdlock);

50 fd = Door_create(servproc, NULL, DOOR_PRIVATE);

51 Pthread_mutex_unlock(&fdlock);

52 unlink(argv[1]);

53 Close(Open(argv[1], O_CREAT | O_RDWR, FILE_MODE));

54 Fattach(fd, argv[1]);

55 /* servproc обслуживает запросы клиентов */

56 for(;;)

57 pause;

58 }

По сравнению с листингом 15.2 было внесено четыре изменения:

1. Убрано объявление дескриптора двери fd (теперь это глобальная переменная, описанная в листинге 15.18).

2. Вызов door_create защищен взаимным исключением (также описанным в листинге 15.18).

3. Вызов door_server_create делается перед созданием двери, при этом указывается процедура создания сервера (my_thread, которая, будет показана позже).

4. В вызове door_create последний аргумент (атрибуты) имеет значение DOOR_PRIVATE вместо 0. Это говорит библиотеке о том, что данная дверь будет иметь собственный пул потоков, называемый частным пулом сервера.

Задание процедуры создания сервера с помощью door_server_create и выделение частного пула сервера с помощью DOOR_PRIVATE осуществляются независимо друг от друга. Возможны четыре ситуации:

1. По умолчанию частный пул сервера и процедура создания сервера отсутствуют. Система создает потоки по мере необходимости и они переходят в пул потоков процесса.

2. Указан флаг DOOR_PRIVATE, но процедура создания сервера отсутствует. Система создает потоки по мере необходимости и они отходят в пул потоков процесса, если относятся к тем дверям, для которых флаг DOOR_PRIVATE не был указан, либо в пул данной двери, если она была создана с флагом DOOR_PRIVATE.

3. Отсутствует частный пул сервера, но указана процедура создания сервера. Процедура создания вызывается при необходимости создания нового потока, который затем переходит в пул потоков процесса.

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

В листинге 15.18 приведен текст двух новых функций: my_create (процедура создания сервера) и my_thread (функция, выполняемая каждым потоком, который создается my_create).

Листинг 15.18. Функции управления потоками

//doors/server6.c

13 pthread_mutex_t fdlock = PTHREAD_MUTEX_INITIALIZER;

14 static int fd = –1; /* дескриптор двери */

15 void *

16 my_thread(void *arg)

17 {

18 int oldstate;

19 door_info_t *iptr = arg;

20 if ((Door_server_proc*)iptr->di_proc == servproc) {

21 Pthread_mutex_lock(&fdlock);

Поделиться с друзьями: