Шрифт:
Интервал:
Закладка:
ПРИМЕЧАНИЕ
Впервые двери были разработаны для распределенной операционной системы Spring. Детали этого проекта доступны по адресу http://www.sun.com/tech/projects/spring. Описание механизма дверей в этой операционной системе можно найти в книге [7].
Затем двери появились в версии Solaris 2.5, хотя единственная страница документации, к ним относящаяся, содержала только предупреждение о том, что двери являются экспериментальным интерфейсом, используемым отдельными приложениями Sun. В Solaris 2.6 описание этого интерфейса занимает уже 8 страниц, но в них он характеризуется как «развивающийся». В будущих версиях Solaris 2.6 описываемый в этой главе интерфейс API может быть изменен. Предварительная версия дверей для Linux уже разрабатывается, детали можно выяснить по адресу http://www.cs.brown.edu/~tor/doors.
Чтобы воспользоваться интерфейсом дверей в Solaris 2.6, нужно подключить соответствующую библиотеку (-ldoor), содержащую функции door_XXX, описываемые в этой главе, и использовать файловую систему ядра (/kernel/sys/doorfs).
Хотя двери поддерживаются только в системе Solaris, мы описываем их достаточно подробно, поскольку это описание позволяет подготовить читателя к удаленному вызову процедур без необходимости обсуждать какие-либо детали сетевого интерфейса. В приложении А мы увидим, что этот интерфейс достаточно быстр — едва ли не быстрее, чем все остальные средства передачи сообщений.
Локальные вызовы процедур являются синхронными (synchronous): вызывающий процесс не получает управление до тех пор, пока не происходит возврат из вызванной процедуры. Потоки могут восприниматься как средство асинхронного вызова процедур: функция (указанная в третьем аргументе pthread_create) выполняется одновременно с вызвавшим процессом. Вызвавший процесс может ожидать завершения вызванного процесса с помощью функции pthread_join. Удаленный вызов процедур может быть как синхронным, так и асинхронным, но мы увидим, что вызовы через двери являются синхронными.
Внутри процесса двери идентифицируются дескрипторами. Извне двери могут идентифицироваться именами в файловой системе. Сервер создает дверь вызовом door_create; аргументом этой функции является указатель на процедуру, которая будет связана с данной дверью, а возвращаемое значение является дескриптором двери. Затем сервер связывает полное имя файла с дескриптором двери с помощью функции fattach. Клиент открывает дверь вызовом open, при этом аргументом функции является полное имя файла, которое сервер связал с дверью, а возвращаемым значением — дескриптор, который будет использоваться клиентом для доступа к двери. Затем клиент может вызывать процедуру с помощью door_call. Естественно, программа, являющаяся сервером для некоторой двери, может являться клиентом для другой.
Мы уже сказали, что вызовы через двери являются синхронными: когда клиент вызывает door_call, возврата из этой функции не происходит до тех пор, пока процедура на сервере не завершит работу (возможно, с ошибкой). Реализация дверей в Solaris связана с потоками. Каждый раз, когда клиент вызывает процедуру сервера, для обработки этого вызова создается новый поток в процессе-сервере. Работа с потоками обычно осуществляется автоматически функциями библиотеки дверей, при этом потоки создаются по мере необходимости, но мы увидим, что сервер может и сам управлять этими потоками, если это требуется. Это также означает, что одна и та же процедура может выполняться сервером для нескольких клиентов, причем для каждого из них будет создан отдельный поток. Такой сервер является параллельным. Поскольку одновременно могут выполняться несколько экземпляров процедуры сервера (каждая из которых представляет собой отдельный поток), содержимое этих процедур должно соответствовать определенным требованиям, которые обычно предъявляются к многопоточным программам.
При удаленном вызове процедуры и данные, и дескрипторы могут быть переданы от клиента к серверу. Обратно также могут быть переданы данные и дескрипторы. Передача дескрипторов вообще является неотъемлемым свойством дверей. Более того, поскольку двери идентифицируются дескрипторами, это позволяет процессу передать дверь другому процессу. Более подробно о передаче дескрипторов будет говориться в разделе 15.8.
Пример
Начнем описание интерфейса дверей с простого примера: клиент передает серверу длинное целое, а сервер возвращает клиенту квадрат этого значения тоже как длинное целое. В листинге 15.1[1] приведен текст программы-клиента (в этом примере мы опускаем множество деталей, большую часть которых мы обсудим далее в тексте главы).
Листинг 15.1 .Клиент передает серверу длинное целое для возведения его в квадрат//doors/client1.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int fd;
6 long ival, oval;
7 door_arg_t arg;
8 if (argc != 3)
9 err_quit("usage: client1 <server-pathname> <integer-value>");
10 fd = Open(argv[1], O_RDWR); /* открываем дверь */
11 /* задаем аргументы и указатель на результат */
12 ival = atol(argv[2]);
13 arg.data_ptr = (char *) &ival; /* аргументы */
14 arg.data_size = sizeof(long); /* размер аргументов */
15 arg.desc_ptr = NULL;
16 arg.desc_num = 0;
17 arg.rbuf = (char *) &oval; /* результат */
18 arg.rsize = sizeof(long); /* размер результата */
19 /* вызываем процедуру на сервере и выводим результат */
20 Door_call(fd, &arg);
21 printf("result: %ldn", oval);
22 exit(0);
23 }
Открываем дверь8-10 Дверь задается полным именем, передаваемым в качестве аргумента командной строки. Она открывается вызовом open. Возвращаемый дескриптор называется дескриптором двери, но часто его самого и называют дверью.
Подготовка аргументов и указателя на результат11-18 Структура arg содержит указатели на аргументы и результат. Поле data_ptr указывает на первый байт аргументов, a data_size содержит количество байтов в аргументах. Два поля desc_ptr и desc_num предназначены для передачи дескрипторов, о чем мы будем подробно говорить в разделе 15.8. rbuf указывает на первый байт буфера результата, a rsize задает его размер.
Вызов процедуры на сервере и вывод результата19-21 Мы вызываем процедуру на сервере с помощью door_call; аргументами этого вызова являются дескриптор двери и указатель на структуру аргументов. После возвращения из этого вызова программа печатает получившийся результат.
Программа-сервер приведена в листинге 15.2. Она состоит из процедуры сервера с именем servproc и функции main.
Листинг 15.2. Сервер, возводящий длинное целое в квадрат//doors/server1.c
1 #include "unpipc.h"
2 void
3 servproc(void *cookie, char *dataptr, size_t datasize,
4 door_desc_t *descptr, size_t ndesc)
5 {
6 long arg, result;
7 arg = *((long *) dataptr);
8 result = arg * arg;
9 Door_return((char *) &result, sizeof(result), NULL, 0);
10 }
11 int
12 main(int argc, char **argv)
13 {
14 int fd;
15 if (argc != 2)
16 err_quit("usage: server1 <server-pathname>");
17 /* создание двери и связывание ее с файлом */
18 fd = Door_create(servproc, NULL, 0);
19 unlink(argv[1]);
20 Close(Open(argv[1], O_CREAT | O_RDWR, FILE_MODE));
21 Fattach(fd, argv[1]);
22 /* функция servproc() обрабатывает все запросы клиентов */
23 for (;;)
24 pause();
25 }
Процедура сервера2-10 Процедура сервера вызывается с пятью аргументами, но мы используем только один из них — dataptr. Он указывает на первый байт аргумента. Аргумент, представляющий собой длинное целое, передается через этот указатель и возводится в квадрат. Управление передается клиенту вместе с результатом вызовом door_return. Первый аргумент указывает на результат, второй задает его размер, а оставшиеся предназначены для возврата дескрипторов.
Создание дескриптора двери и связывание с ним файла17-21 Дескриптор двери создается вызовом door_create. Первый аргумент является указателем на функцию, соответствующую этой двери (servproc). После получения этого дескриптора его нужно связать с некоторым именем в файловой системе, поскольку оно будет использоваться клиентом для подключения к этой двери. Делается это путем создания обычного файла в файловой системе (сначала мы вызываем unlink, на тот случай, если такой файл уже существует, причём возможная ошибка игнорируется) и вызова fattach — функции SVR4, связывающей дескриптор с полным именем файла.
Главный поток сервера ничего не делает22-24 Главный поток сервера блокируется при вызове pause. Вся функциональность обеспечивается функцией servproc, которая будет запускаться как отдельный поток каждый раз при получении запроса клиента.
- Delphi. Учимся на примерах - Сергей Парижский - Программирование
- Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ - Хелен Борри - Программирование
- Программирование на Python с нуля - Максим Кононенко - Программирование
- C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц - Программирование
- Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп - Программирование