NCS FreeBSD


11.1. ВВЕДЕНИЕ

NCS (Network Computing System) позволяет распределять вычис-
ления по машинам сети. NCS была разработана в 1987 фирмой
Apollo,а затем доработана фирмой HP (Hewlett Packard).
Разработка NCS базировалась на архитектуре NCA (Network
Computing Architecture), предназначенной для конструирования
распределенных систем. NCA определяет протокол вызовов удален-
ных процедур.


11.2. ФУНКЦИОНАЛЬНЫЕ ВОЗМОЖНОСТИ

11.2.1. Предлагаемые возможности

NCS использует объектно-ориентированный подход для определе-
ния распределенных ресурсов. Любая удаленная операция опреде-
ляется в терминах операций (вызовов процедур) над объектами
(активными или пассивными ресурсами).
Система NCS на рабочих станциях UNIX состоит из трех состав-
ных частей : библиотека RPC, язык определения интерфейса сети
и служба локализации.

Библиотека RPC (Remote Procedure Call) позволяет вызывать
удаленные процедуры. Логический уровень (заглушка или адапта-
тор клиента) инкапсулирует параметры вызовов, отправляет сооб-
щение через сеть, ждет ответа и декодирует результаты.
Со стороны сервера, заглушка или адаптатор сервера, декоди-
рует параметры вызова, вызывает требуемую процедуру, инкапсу-
лирует результаты и передает клиенту сообщение.

Кроме того, заглушки управляют преобразованием внутреннего
представления данных между клиентом и сервером : тип представ-
ления клиента передается серверу при вызове, а сервер сам ре-
шает, преобразовывать данные или нет. При передаче выходных
параметров выполняется аналогичная операция. Таким образом,
преобразованием данных, в случае необходимости, всегда занима-
ется получатель.

Между прочим, механизм управления ошибками, включенный в
среду RPC, обеспечивает хорошую обработку сбоев функционирова-
ния сервера и сети.

Язык определения сетевых интерфейсов (NIDL : Network
Interface Definition Language) позволяет описывать интерфейс
между локальной и распределенной частями прикладной программы.
Программы на этом язык, синтаксис которого совпадает, по выбо-
ру, с синтаксисом Си или Паскаля, передаются препроцессору,
который порождает заглушки, используемые при вызове RPC.

Служба локализации (Location Broker или агент по локализа-
ции) позволяет находить сетевые адреса ресурсов. Location
Broker сам по себе состоит из нескольких агентов, выполняющих
различные функции :
- Local Location Broker (LLB) : хранит информацию, связанную
с локальными объектами; выполняется на всех машинах, обес-
печивающих возможности NCS;
- Global Location Broker (GLB) : обрабатывает сведения о ре-
сурсах в сети. Он управляет базой данных и может быть для
обеспечения надежности продублирован. Информация о ресур-
сах обновляется динамически : информация о запускаемом
сервере добавляется к базе, а о сервере, закончившем свою
работу (нормально или вследствие сбоя) удаляется.
- Локальный агент : библиотека, которая обеспечивает интер-
фейс между прикладной программой и службой локализации
(LLB и GLB).

Заметим, что вопреки общераспространенному мнению, NCS не
выбирает автоматически оптимальный сервер из числа доступных.
Однако, эта функциональная возможность будет, без сомнения,
добавлена в следующей версии.

А пока-что обязанность сохранить список серверов и запустить
механизм, определяющий лучший среди них, лежит на прикладной
программе-клиенте.

В отличие от RPC фирмы Sun, NCS позволяет локализовывать ре-
сурсы динамически (RPC фирмы Sun делает это только статически)
и обеспечивает преобразование двоичных данных.

11.2.2. Механизмы функционирования

Модель клиент-сервер
--------------------

Как и RPC фирмы Sun, NCS позволяет конструировать прикладные
программы в соответствии с моделью клиент-сервер (Рис. 11.1.).


КЛИЕНТ СЕРВЕР
---------------¬ ---------------¬
¦ Запрос на вы-¦ СЕТЬ ¦ ¦
¦ полнение про-¦ ---------------¬ ¦ ¦
¦ цедуры NCS ¦ ¦ ¦ ¦ ¦
¦ +---->¦ Передача зап-+--->¦ Получение зап¦
¦ Ожидание от- ¦ ¦ роса ¦ ¦роса NCS. Вы- ¦
¦ вета ¦ ¦ ¦ ¦зов соответств¦
¦ ¦ ¦ ¦ ¦ующей процедур¦
¦ ¦ ¦ ¦ ¦ы ¦
¦ Получение от-¦<----+ Передача от- ¦<---+ Посылка отве-¦
¦ вета ¦ ¦ вета ¦ ¦та ¦
L--------------- L--------------- L---------------

Рис. 11.1. - Механизм клиент - сервер и NCS.

Объекты, ресурсы и службы
-------------------------

Объект или ресурс - это нечто, с чем связаны определенные
операции; файл, принтер, процессор - все это примеры объектов.
Каждый объект имеет свой тип. Программы работают с объектами
через один или несколько интерфейсов. Интерфейс - это группа
операций (процедур), работающих с объектом.
Ресурс однозначно определяется тремя номерами : UUID
(Universal Unique IDentifier) : - UUID для объекта - UUID
для типа объекта - UUID для интерфейса доступа к объекту.

На практике, эти номера часто совпадают для одного и того же
ресурса. UUID - уникальный номер, порождаемый сервисной прог-
раммой uuid_gen. Уникальность обеспечивается использованием
при формировании номера текущих даты и времени.

Понятие службы является более общим : процесс-сервер несет
службу. Служба определяется своим адресом, имеющим формат но-
мер семейства : номер порта. Мы используем семейство IP, кото-
рому соответствует числовое значение - два.

Локализация ресурса
------------------

Существует три возможности :
- клиент знает имя машины и номер порта сервера : в таком
случае, он обращается непосредственно к ресурсу
- клиент знает только имя сервера. Процесс-сервер зарегист-
рирован у Local Location Broker. Клиент обращается к
Broker'у и тот сообщает ему номер порта, определив его по
UUID, переданному клиентом
- клиент вообще не знает где находится сервер. Процесс-сер-
вер зарегистрирован как у Local Location Broker,так и у
Global Location Broker. Клиент обращается к Global
Location Broker (адрес которого можно получить с помощью
"широковещания" или чтения файла glb_site.txt). Global
Location Broker передает адреса всех серверов (в порядке
регистрации), соответствующих указанному UUID.

Рисунок 11.2. иллюстрирует этот механизм.


-------------¬ -------------¬
¦ ---------¬ ¦ ¦ ---------¬ ¦
¦ ¦ 1 +-+----------3--------+>¦ 2 ¦ ¦
¦ L--------+-+---------¬ --------+-+--------- ¦
L------T------ ¦ ¦ L------T------
¦ ¦ ¦ ¦
¦ 2 ¦ 1a
¦ ¦ 1b ¦
2 ¦ ¦ v
¦ ¦ ¦ --------¬
L-----------¬ L-+---------->¦ ¦
v v L--------
----------¬
¦ ¦
L----------

Рис. 11.2. - Обмен информацией между клиентом, сервером и
Location Broker.
1 - Программа-клиент
2 - Программа-сервер

1a: Сервер регистрируется у Local Location Broker
1b: Сервер регистрируется у Global Location Broker
2: Клиент обращается к Local Location Broker сервера,
если он знает его адрес или к Global Location Broker
3: Клиент адресуется прямо к серверу

Заглушка сервера и заглушка клиента
-----------------------------------

Концепции заглушки клиента и заглушки сервера были введены в
главе 10 (RPC фирмы Sun). Что собой представляют заглушка кли-
ента и заглушка сервера в NCS ?

- Заглушка клиента :
- обеспечивает связывание с сервером
- заносит входные параметры в сообщение
- отправляет сообщение заглушке сервера
- получает ответное сообщение
- извлекает из него выходные параметры
- преобразует данные
- возвращает их программе-клиенту.

- Заглушка сервера :
- извлекает входные параметры из полученного сообщения
- преобразует данные
- вызывает процедуру, которая выполняет запрошенную операцию
- заносит выходные параметры в пакет
- посылает пакет заглушке клиента.

Протокол
--------

Текущая версия NCS реализована как надстройка над транспорт-
ным протоколом UDP и транспортным протоколом Domain фирмы
Apollo. Надежность обменов обеспечивает NCS за счет своих
внутренних механизмов (управление ошибками встроено в протокол
NCS).

Понятия handle и binding
------------------------

Handle (или скрытый дескриптор) - это указатель на структу-
ру, которая содержит сведения, идентифицирующие и локализующие
ресурс, в частности UUID и адрес ресурса (рис. 11.3.). Заме-
тим, что это понятие уже встречалось нам при рассмотрении RPC
фирмы Sun (handle клиента и транспортный handle), XDR (XDR
handle) и NFS (handle файла).

Binding (связывание) связывает сервер с handle, обновляя
значение адреса сервера и номера порта службы в структуре, ад-
ресуемой handle.
Заметим, что понятие binding'а отличается от одноименного
понятия, используемого для sockets (связь с локальным адре-
сом).
Существует частичное связывание (только идентификаторы UUID
или UUID и имя сервера в handle) и полное связывание (UUID,
имя сервера и номер порта службы в handle).


-----------------¬ ------------------¬
¦ Handle +------>¦ ¦
L----------------- +-----------------+
¦ Идентификатор ¦
¦ UUID ¦
+-----------------+
¦ ¦
+-----------------+
¦ Семейство ¦
¦ адресов ¦
+-----------------+
¦ Адрес сети ¦
+-----------------+
¦ Номер порта ¦
+-----------------+
¦ ¦
L------------------

Рис. 11.3. - Понятие handle.

Существуют понятия явного (стандартно) и неявного handle :
- явный handle передается при вызовах удаленных процедур.
Это позволяет клиенту вызывать одновременно несколько сер-
веров (указывая handle соответствующий серверу), а серверу
идентифицировать клиентов. С другой стороны, вызовы проце-
дур при этом не являются прозрачными (в том смысле, что
вызов удаленных процедур при этом отличается от вызова ло-
кальных), поскольку при вызове приходится указывать handle
;
- неявный handle - это глобальная переменная. Она не переда-
ется серверу и вызовы удаленных процедур выполняются в
точности так же, как и вызовы локальных.

Аналогичным образом возникают понятия ручного и автоматичес-
кого связывания (binding) :
- при ручном связывании связь между клиентом и сервером ус-
танавливается только один раз. При этом программист должен
явно специфицировать эту связь перед вызовом удаленной
подпрограммы, так что она не является прозрачной для прик-
ладной программы ;
- автоматическое связывание выполняется при каждом вызове
удаленной процедуры. Она осуществляется модулями библиоте-
ки NCS (программист должен написать свою процедуру, кото-
рая и будет вызываться автоматически). При этом, в отличие
от ручного связывания прикладная программа не изменяется.
В каждом вызове удаленной процедуры может участвовать но-
вый сервер.

Семантика обращений
-------------------

Описание семантических аспектов различных видов обращений
см. в главе 10, параграфе 10.2.2. NCS поддерживает обращения
вида "самое большее один раз" (at-most_once). В таких случаях,
сервер запоминает ответ процедуры после первого к ней обраще-
ния и возвращает этот результат, не выполняя процедуры, в том
случае, если она вызывается несколько раз.
Этот режим работает по умолчанию, однако программист может
явно указать, что процедура является идемпотентной, что позво-
ляет сделать процесс вычисления более быстрым (см. параграф
11.4.).

Прозрачность
------------

Как было уже сказано в параграфе, посвященном понятиям
handle и binding, прозрачность (в смысле идентичности вызовов
локальных и удаленных процедур) обеспечивается при работе в
режимах неявного handle и автоматического связывания. Для это-
го, чтобы указать эти режимы работы в языке NIDL надо :
- определить глобальную переменную с атрибутом implicit handle;
- указать атрибут handle в определении типа переменной handle.

Эта возможность иллюстрируется примером в параграфе 11.3.5.

Прозрачность обеспечивается за счет двух неприятных явлений :
- "статус" становится неуправляемым : см. параграф 11.3.5.;
- связь между клиентом и сервером устанавливается при каждом
обращении к удаленной процедуры - при этом тратится время
и падает производительность. Этот эффект можно смягчить,
если организовать в прикладной программе специальный кэш
или запомнить полученный handle (в том случае, если все
время используется один и тот же сервер).

Заметим, что прозрачности можно достичь используя стандарт-
ные возможности NIDL - т.е. явный handle и ручное связывание.
Этого можно достичь за счет дополнительного уровня инкапсуля-
ции в процедурах, вызывающих программы NCS (см. пример в па-
раграфе 10.3.4 - глава, посвященная RPC фирмы Sun).

Управление темпоризаторами
--------------------------

Вызывая удаленную процедуру, NCS создает темпоризатор. В от-
личие от RPC фирмы Sun этот темпоризатор не ждет ответа, а
лишь организует задержку для организации и поддержания связи.
При возникновении тайм-аута, NCS запрашивает сервера о его
состоянии, и в случае активности последнего продолжает ждать.
Таким образом, не делается никаких предположений о времени вы-
полнения удаленной операции.

11.3. ИСПОЛЬЗОВАНИЕ

11.3.1. Текущее использование

Принципы эксплуатации
---------------------

NCS поддерживает языки Си и Паскаль.

Сервер должен выполнить следующие операции :
- инициализировать обработчик ошибок (принадлежащий NCS);
- связать сокет с соответствующей службой;
- зарегистрировать интерфейс с RPC;
- зарегистрировать интерфейс у Local Location Broker или
Global Location Broker;
- ожидать запросы клиентов.

Клиент должен выполнить следующие операции :
- инициализировать обработчик ошибок;
- найти адрес службы;
- создать handle и обеспечить связь (binding);
- вызвать удаленную процедуру.

Препроцессор NIDL порождает по описанию интерфейса процедур,
составленному на языке NIDL, файл-заголовок,заглушку клиента и
заглушку сервера. В отличие от RPC фирмы Sun, где использова-
ние RPCGEN не является обязательным, использование NIDL в сре-
де NCS обязательно.

Используя NCS, приходится использовать NIDL.


Компилирование программ
-----------------------

Для преобразования символов $ (не воспринимаемых компилято-
ром Си) в символы _ (подчеркивания) следует использовать преп-
роцессор :

# cc -tp,/etc/ncs/cpp

Файл включений
--------------

При программировании процедур NCS используется файл включе-
ний ncs.h. При запуске компилятора следует указывать параметр
-I/usr/include/idl/c.
Типы данных ndr_$ определяются в файле "nbase.h". Эти типы
данных рекомендуется использовать для обеспечения мобильности
прикладной программы (возможности переноса ее на машины других
типов).

ПРОГРАММА 68

/*Файл включения ncs.h*/

#include <stdoi.h>
#include "nbase.h"
#include "lb.h"
#include "socket.h"
#include <ppfm.h>
#include <sys/times.h>
#include <sys/param.h>
#include <sys/types.h>
/*Файл, поpождаемый NIDL на базе файла ser.idl*/
#include "ser.h"
/*опpеделение сеpвеpа или его отсутствие, в зависимости от пpикладной
пpогpаммы*/
#define SERVEUR "ip:ordinam"


Форматирование сообщения об ошибке
----------------------------------

Формируется процедура error_text(), которая передает сообще-
ние об ошибке, замеченной системой NCS.


ПРОГРАММА 69

/*Пpоцедуpа, выдающая сообщение об ошибке*/

#include "ncs.h"
char *error_text(st)
status_$t st;
{
static char buff[200];
extern char *error_$c_text();
return (error_$c_text(st, buff, sizeof buff));
}


11.3.2. Примитивы библиотеки NCS

Основные примитивы
------------------

Общие замечания :
- во всех запросах, требующих указания UUID в качестве пара-
метра, можно указать значение uuid_$nil. Это значение ука-
зывает на то, что идентификатор должен игнорироваться ;
- в описании примитивов NCS присутствуют ссылки на интерфейс
службы, определяемой с помощью NIDL. NIDL порождает конс-
танты, связанные с именем интерфейса serv : serv_vl$server
_epv, serv_vl$ if_spec, serv_vl$manager_epv ...

Различаются запросы вида rpc_$ к библиотеке RPC и запросы
вида lb_$ - к агентам по локализации.

Запросы клиента вида rpc_$

- Создание handle и связывание

handle_t rpc_$bind(object, sockadr, slength, status)
uuid_$t *object; /* UUID или uuid_$nil */
socket_$addr_t *sockadr; /* адрес сокета сервера */
unsigned long slength; /* длина sockadr */
status_$t *status; /* статус возврата */

Эта функция создает handle, который представляет объект,ад-
ресуемый UUID object, и выполняет связывание. Пользователю
следует указать адрес сервера. Номер порта указывать необяза-
тельно : если он не указан, система определит его при первом
обращении к RPC с помощью запроса к Location Broker.

- Освобождение handle

void rpc_$free_handle(handle,status)
handle_t handle; /* handle RPC */
status_$t *status; /* статус возврата */

Эта функция освобождает handle RPC и связанные с ним ресурсы.

Запросы сервера типа rpc_$

- Создание сокета

void rpc_$use_family(family, sockadr, slength, status)
unsigned long family; /* socket_$internet,или 2 */
socket_$addr_t *sockadr; /* адрес сокета сервера */
unsigned long *slength; /* длина sockadr */
status_$t *status; /* статус возврата */

Эта функция создает слушающий сокет сервера - без указания
номера порта.

void rpc_$use_family_wk(family, ifspec, sockadr, slength,
status)
unsigned long family; /* socket_$internet, или 2 */
rpc_$if_spec_t *ifspec; /* интерфейс: serv_vl$if_spec */
socket_$addr_t *sockadr; /* адрес сокета сервера */
unsigned long *slength; /* длина sockadr */
status_$t *status; /* статус возврата */

Эта функция создает слушающий сокет сервера - при этом ука-
зывается номер порта. Номер порта при этом должен быть указан
в интерфейсе NIDL (параметр ifspec).

- Регистрация службы RPC

void rpc_$register_mgr(type, ifspec, sepv, mepv, status)
uuid_$t *object; /* UUID или uuid_$nil */
rpc_$if_spec_t *ifspec; /* интерфейс:serv_vl$if_spec */
rpc_$generic_epv_t sepv; /* serv_vl$server_epv */
rpc_$mgr_epv_t mepv; /* serv_vl$manager_epv */
status_$t *status; /* статус возврата */

Эта функция регистрирует процедуры службы. Эти процедуры ак-
тивируются при каждом вызове удаленной процедуры клиентом.

void rpc_$register_object(object, type, status)
uuid_$t *object; /* UUID или uuid_$nil */
uuid_$t *type ; /* UUID типа объекта */
status_$t *status; /* статус возврата */

Этот вызов использует примитив rpc_$register_mgr(), в слу-
чае, если регистрируется объект, тип которого точно указан (не
uuid_$nil).

- Отключение службы

void rpc_$register_mgr(ifspec, status)
rpc_$if_spec_t *ifspec; /* интерфейс:
serv_vl$if_spec */
status_$t *status; /* статус возврата */

Этот примитив отключает службу. Соответствующие ей процедуры
становятся недоступными.

- Ожидание запросов клиентов

void rpc_$listen(max_calls, status)
unsigned long max_calls /* число одновременных вызовов :
1 */
status_$t *status; /* статус возврата */

Сервер ждет запросов клиентов в бесконечном цикле. Параметр
max_calls используется лишь в том случае, если можно одновре-
менно активировать несколько серверов, работающих параллельно
(см. главу 12).

Запросы типа lb_$

- Поиск объектов с помощью Global Location Broker

void lb_$lookup_object(object, lookup_handle, max_results,
num_results, results, status)
uuid_$t *object; /* UUID или uuid_$nil */
lb_$lookup_handle_t *lookup_handle; /* lb_$default */
unsigned long max_results /* максимальный размер резуль-
татов */
unsigned long *num_results /* число найденных
объектов */
lb_$entry_t results[]; /* массив результатов */
status_$t *status; /* статус возврата */

Эта функция организует поиск UUID объекта с помощью Global
Location Broker. Она сообщает адреса всех серверов. Если ищет-
ся только один сервер (max_results=1), то функция сообщает ад-
рес первого зарегистрированного сервера.

- Поиск объекта с помощью Local Location Broker

void lb_$lookup_object_local(object,location,
location_length, lookup_handle,max_results,
num_results, results, status)
uuid_$t *object; /* UUID или uuid_$nil */
socket_$addr_t *location /* адрес сервера */
unsigned long location_length; /* длина адреса */
lb_$lookup_handle_t *lookup_handle; /* lb_$default */
unsigned long max_results /* максимальный размер резуль-
татов */
unsigned long *num_results /* число найденных объектов */
lb_$entry_t results[]; /* массив результатов */
status_$t *status; /* статус возврата */

Эта функция организует поиск UUID объекта с помощью Local
Location Broker того сервера, адрес которого указал пользова-
тель.

- Поиск типа объекта с помощью Global Location Broker

void lb_$lookup_type(type, lookup_handle, max_results,
num_results, results, status)
uuid_$t *object; /* UUID типа объекта */
lb_$lookup_handle_t *lookup_handle; /* lb_$default */
unsigned long max_results /* максимальный размер резуль-
татов */
unsigned long *num_results /* число найденных
объектов */
lb_$entry_t results[]; /* массив результатов */
status_$t *status; /* статус возврата */

Эта функция организует поиск UUID типа объекта с помощью
Global Location Broker. Она сообщает адреса всех серверов,
удовлетворяющих запросу.

- Поиск интерфейса объекта с помощью Global Location Broker

void lb_$lookup_interface(interface, lookup_handle,
max_results, num_results, results, status)
uuid_$t *interface; /* UUID интерфейса */
lb_$lookup_handle_t *lookup_handle; /* lb_$default */
unsigned long max_results /* максимальный размер резуль-
татов */
unsigned long *num_results /* число найденных
объектов */
lb_$entry_t results[]; /* массив результатов */
status_$t *status; /* статус возврата */

Эта функция организует поиск UUID интерфейса объекта с по-
мощью Global Location Broker. Она сообщает адреса всех серве-
ров, удовлетворяющих запросу.

- Регистрация объекта

void lb_$register(object, obj_type, obj_interface, flags,
annotation, location, location_length, entry, status)
uuid_$t *object; /* UUID объекта или uuid_$nil */
uuid_$t *obj_type; /* UUID типа или uuid_$nil */
uuid_$t *obj_interface; /* UUID интерфейса */
lb_$server_flag_t *flags; /* 0 или lb_$server_flag_local*/
char annotation[64]; /* управляющая информация */
unsigned long location_length; /* длина адреса */
lb_$entry_t *entry; /* копия регистрационной записи */
status_$t *status; /* статус возврата */

Эта функция регистрирует объект и интерфейс у Local Location
Broker (flags имеет значение lb_$server_flag_local) или у
Local Location Broker и одновременно у Global Location Broker
(значение flags равно 0). Обязательным является только пара-
метр UUID интерфейса (serv_vl$if_spec.id).

- Отключение объекта

void lb_$unregister(entry, status)
lb_$entry_t *entry; /* копия регистрационной записи */
status_$t *status; /* статус возврата */

Эта функция уничтожает регистрационную запись.


Несколько дополнительных процедур
---------------------------------

Различаются вызовы следующих типов : rrpc_$,socket_$,uuid_$,
error_$ и pfm_$.

- Вызовы rrpc_$ : они позволяют клиенту контролировать сервер

void rrpc_$are_you_there(handle, status)
handle_t handle; /* handle RPC */
status_$t *status; /* статус возврата */

Эта функция позволяет проверить, отличает ли сервер на запрос :
если все в порядке переменная status получает значение status_$ok.

void rrpc_$shut_down(handle, status)
handle_t handle; /* handle RPC */
status_$t *status; /* статус возврата */

Эта функция позволяет клиенту остановить сервер, при усло-
вии, что последний заранее позволил это сделать с помощью об-
ращения к примитиву rpc_$allow_remote_shutdown().

- Вызовы типа socket_$ : выполняют обработку, связанную с
адресами сокетов

void socket_$to_name(sockadr, slength, name, nlength,
port, status)
socket_$addr_t *sockaddr; /* адрес сокета */
unsigned long slength; /* длина адреса */
socket_$string_t name; /* цепочка ip:host[port] */
unsigned long *nlength; /* длина цепочки */
unsigned long *port; /* номер порта */
status_$t *status; /* статус возврата */

Этот примитив преобразует адрес сокета в строку символов,
содержащую семейство адреса, имя машины и номер порта.

void socket_$from_name(family, name, nlength, port,
sockadr, slength, status)
unsigned long family; /* семейство адреса */
socket_$string_t name; /* цепочка ip:host[port] */
unsigned long *nlength; /* длина цепочки */
unsigned long *port; /* номер порта */
socket_$addr_t *sockaddr; /* адрес сокета */
unsigned long slength; /* длина адреса */
status_$t *status; /* статус возврата */

Эта функция преобразует строку, содержащую семейство адреса,
имя машины и номер порта в адрес сокета.

long socket_$family_from_name(name, nlength, status)
socket_$string_t name; /* цепочка ip:host[port] */
unsigned long *nlength; /* длина цепочки */
status_$t *status; /* статус возврата */

Эта функция возвращает значение семейства адреса или 2 для
семейства IP.

boolean socket_$valid_family(family, status) unsigned long
family; /* номер, связанный с семейством */
status_$t *status; /* статус возврата */

Эта функция проверяет, правильно ли указан номер семейства :
если все верно функция выдает значение TRUE.

- Вызовы типа uuid_$ : они позволяют порождать и обрабаты-
вать UUID.

void uuid_$gen(uuid)
uuid_$t *uuid; /* UUID */

Этот примитив порождает UUID.

void uuid_$decode(s, uuid, status)
uuid_$string_t s; /* строка, представляющая UUID */
uuid_$t *uuid; /* значение UUID */
status_$t *status; /* статус возврата */

Эта функция преобразует символьную строку, представляющую
UUID в значение.

void uuid_$encode(uuid, s)
uuid_$t *uuid; /* значение UUID */
uuid_$string_t s; /* строка, представляющая UUID */

Эта функция реализует операцию, обратную предыдущей.

- Вызовы типа _error_$ : преобразуют статус ошибки в сообщение

char *error_$c_text(status, message, messagemax)
status_$t *status; /* статус возврата функций */
char *message; /* сообщение, описывающее статус */
int messagemax; /* число символов в сообщении */

Эта функция возвращает символьную строку максимальной длины
messagemax, которая описывает значение параметра статуса. Эта
функция используется в процедуре error_text().

- Вызовы типа pfm_$ : эти вызовы управляют сигналами, ошиб-
ками и исключениями (condition)

status_$t pfm_$cleanup(cr)
pfm_$cleanup_rec *cr;

Эта функция создает обработчик, который освобождает ресурсы
при возникновении фатальной ошибки, например при получении
сигнала.

void pfm_$init(flags)
unsigned long flags; /* pfm_$init_signal_handlers */

Эта функция инициализирует процедуры управления ошибками
NCS. Сигналы будут перехватываться и обрабатываться процедура-
ми pfm.

void pfm_$signal(fault_signal)
status_$t fault_signal; /* статус ошибки */

Эта функция вызывается, чтобы предупредить процесс об ошиб-
ке. Она завершает последовательность процедур управления ошиб-
ками (см. примеры).

11.3.3. Использование NIDL

Принципы эксплуатации
---------------------

NIDL - это одновременно язык и препроцессор. Он порождает
файлы с программами на языке Си (рис. 11.4.). Он вызывается
следующим образом (в HP-UX) :

#nidl ser.idl -m -idir /usr/include/idl

------------¬
-------> 3 ¦
¦ ¦ ser.h ¦
----------¬ --------¬¦ L------------
¦ 1 ¦ ¦ +- -------------¬
¦ ser.idl +---> 2 +-------> 4 ¦
L---------- ¦ NIDL +¬ ¦ser.cstub.c ¦
L--------¦ ¦ser_cswtch.c¦
¦ L-------------
¦ -------------¬
¦ ¦ 5 ¦
L------>ser_sstub.c ¦
L-------------

Рис. 11.4. - Файлы, порождаемые NIDL.
1 - Определение интерфейса на языке NIDL
2 - Компилятор NIDL
3 - Файл-заголовок
4 - Файлы клиента
5 - Файл сервера

Файл ser_cswtch.c используется только в случае дублирования
серверов. Его надо сохранить для редактирования связей клиен-
та.

Пример файла NIDL

Ниже приведен пример файла NIDL для программы, вычисляющей
сумму двух целых чисел. Таким образом, здесь повторяется при-
мер, приведенный для RPC фирмы Sun.

ПРОГРАММА 70

/*Пpимеp файла NIDL : сумма двух целых чисел*/

/*Опpеделение интеpфейса : UUID, веpсия, пpоцедуpы*/
%c
[uuid(4448ee491000.0d.00.00.fe.da.00.00.00), version(1)]
interface intservice
{
/*Опpеделение пpоцедуp интеpфейса*/
[idempotent] int spserv-ncs(handle_t [in] h,
int [in] x1, int [in] x2);


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

Описывается интерфейс intservice,содержащий процедуру spserv
_ncs(), которая возвращает целое число.
Указывается атрибут idempotent, который указывает на то, что
процедуру можно вызывать несколько раз с одним и тем же ре-
зультатом.

Указываются входные (in) и выходные (out) параметры процедуры :
в данном конкретном примере нет выходных параметров. Заметим,
что синтаксис задания параметров подобен синтаксису определе-
ния локальной процедуры (нет необходимости оформлять структу-
ру, как это было в случае RPCGEN). Добавляется входной пара-
метр h типа handle_t : в данном случае используется явный
handle.


Использование

До выполнения программы main.c, которая содержит обращение к
удаленной подпрограмме spserv() надо выполнить следующие дейс-
твия :
- описать интерфейс подпрограммы spserv() в файле ser.idl;
- откомпилировать файл ser.idl с помощью NIDL; в результате
компиляции создаются файлы заглушки клиента (ser_cstub.c и
ser_cswitch.c), файл заглушки сервера (ser_sstub.c) и файл
ser.h, содержащий определения.
- написать файл client.c (или handle.c, если выбраны опции
неявного handle и автоматического связывания), в котором
реализуется связь клиента и сервера и вызов удаленной про-
цедуры;
- написать файл serveur.c, в котором регистрируется служба;
- написать программу manager.c, которая ссылается на процеду-
ру NCS. Начальная программа spserv() записывается в файл
sp.c;
- откомпилировать программу клиента и отредактировать связи
(main.o, client.o, ser_cstub.o, ser_cswtch.o);
- откомпилировать программу сервера и отредактировать связи
(serveur.o,manager.o,ser_sstub.o и sp.o).

Принципиальная схема разработки прикладной программы-клиента
и прикладной программы-сервера изображена на рис. 11.5.

---------¬ ----------------------------¬ ---------¬
¦ 1 ¦ ¦ 2 ¦ ¦ 3 ¦
L---T----- L--------------T------------- L----T----
¦ ---v--¬ ¦
¦ ¦NIDL ¦ ¦
¦ / L------ ¦
¦ ---v-¬ --v--¬ ¦
¦ ¦ 4 ¦ ¦ 5 ¦ ¦
¦ L--T-- L-T--- ¦
----v----¬ -----v---¬ ----v----¬ ----v----¬
¦ 6 ¦ ¦ 6 ¦ ¦ 6 ¦ ¦ 6 ¦
L---T----- L----T---- L---T----- L---T-----
----v----¬ -----v---¬ ----v----¬ ----v----¬
¦ 7 ¦ ¦ 8 ¦ ¦ 9 ¦ ¦ 10 ¦
L--------- L--------- L--------- L---------
/ /
-v-----v-¬ -v------v¬
¦ 11 ¦ ¦ 11 ¦
L---T----- L----T----
----v----¬ -----v---¬
¦ 12 ¦ ¦ 13 ¦
L--------- L---------

Рис. 11.5. - Принципы разработки прикладных программ с по-
мощью NIDL.
1 - Прикладная программа-клиент
2 - Определение интерфейса на языке NIDL
3 - Прикладная программа-сервер
4 - Заглушка клиента
5 - Заглушка сервера
6 - Компилятор Си
7 - Об'ектный модуль прикладной программы
8 - Об'ектный модуль заглушки клиента
9 - Об'ектный модуль заглушки сервера
10 - Об'ектные модули процедур
11 - Редактор связей
12 - Программа-клиент
13 - Программа-сервер


Понятие атрибута

В текстах файлов NIDL слова, заключенные в квадратные скобки
обозначают атрибуты. В частности, приведенные ниже атрибуты
имеют следующее значение :
- in : входной параметр процедуры
- out : выходной параметр процедуры
- last_is : последний элемент массива
- max_is : максимальное число элементов массива
- maybe : процедура без выходных параметров
- idempotent : процедура, которую можно вызывать несколько
раз с одним и тем же результатом
- handle : атрибут типа данных, обуславливающий автоматичес-
кое связывание
- implicit_handle : глобальная переменная, используемая в ка-
честве handle
- comm_status : параметр, используемый для управления стату-
сом.

Типы данных обрабатываемые NIDL

NIDL ограничивает использование некоторых типов данных - эти
типы данных следует использовать с осторожностью :
- не имеющий размерности массив - элемент структуры должен
быть последним элементом структуры
- недопустимы указатели на элемент структуры или объединения
- недопустимы указатели на указатели
- не имеющий размерности массив - элемент матрицы должен
быть ее первым элементом.

Эти ограничения объяснимы : у клиента и сервера разные
пространства адресации и при использовании,например, указате-
лей, библиотека RPC должна передавать всю совокупность исполь-
зуемых данных. NCS включает в себя механизмы, позволяющие
обойти эти ограничения, но их довольно трудно реализовать.
Фактически, для соответствующих типов данных надо указать ат-
рибут transmit_as и написать процедуры, преобразующие эти
сложные типы данных в типы, известные NIDL.

Кроме того, мы рекомендуем :
- никогда не использовать непроинициализированные указатели
(со значением NULL) при обращении к NCS - это приводит к
ошибке времени выполнения;
- использовать массивы, а не указатели (это касается и языка
Си), поскольку массивы легче обрабатывать;
- указать атрибут max_is для выходного параметра типа массив
(если атрибут last_is не указан), поскольку этот атрибут
позволяет заглушке сервера определить размер памяти, кото-
рую надо распределить под результаты.

Практические итоги
------------------

Программист должен написать четыре модуля :
1) файл интерфейса NIDL ser.idl;
2) программу, обеспечивающую связь клиента с сервером и вы-
зов удаленных процедур - файл client.c (явный handle и
ручное связывание) или файл handle.c (неявный handle и
автоматическое связывание);
3) программу, регистрирующую службы в библиотеке RPC и у
агентов локализации - файл serveur.c;
4) программу, ссылающуюся на вызываемые процедуры - файл
manager.c.

На рисунках 11.6,11.7. и 11.8. изображены файлы, порождаемые
NIDL и программы, написанные разработчиком.

-----------------¬
¦ 1 ¦
¦-------------¬ ¦
¦¦ ser.h +--+---------------¬
¦L------------- ¦ ¦
¦-------------¬ ¦ ¦
¦¦ser_cstub.c +--+----------¬ ¦
¦L------------- ¦ ¦ ¦
¦-------------¬ ¦ ¦ -v---------------¬
¦¦ser_cswtch.c+--+----¬ L-->¦ ¦
¦L------------- ¦ L-------->¦ 3 ¦
¦ ¦ --->¦ ¦
L----------------- ¦ L-----------------
-----------------¬ ¦
¦ 2 ¦ ¦
¦ ------------¬ ¦ ¦
¦ ¦ client.c ¦ ¦ ¦
¦ ¦ и +--+-----------
¦ ¦ handle.c ¦ ¦
¦ L------------ ¦
L-----------------

Рис. 11.6. - Порождение прикладной программы клиента.
1 - Создаются компилятором NIDL по описанию из ser.idl
2 - Должны быть написаны пользователем
3 - Прикладная программа-клиент

----------------¬
¦ 1 ¦
¦------------¬ ¦
¦¦ ser.h +--+--------¬
¦L------------ ¦ ¦
¦------------¬ ¦ ¦
¦¦ser_sstub.c+--+-----¬ ¦
¦L------------ ¦ ¦ ¦
¦ ¦ ¦ ¦ ----------¬
L---------------- ¦ L------->¦ ¦
L---------->¦ 3 ¦
----------------¬ -------->¦ ¦
¦ 2 ¦ ¦ ----->L----------
¦------------¬ ¦ ¦ ¦
¦¦serveur.c +--+--------- ¦
¦L------------ ¦ ¦
¦------------¬ ¦ ¦
¦¦manager.c +--+------------
¦L------------ ¦
¦ ¦
L----------------

Рис. 11.7. - Порождение прикладной программы сервера.
1 - Создаются компилятором NIDL по описанию из ser.idl
2 - Должны быть написаны пользователем
3 - Прикладная программа-сервер

1 2
-------------------¬ ------------------¬
¦ ---------¬ ¦ ¦ -----------¬ ¦
¦ 3 ¦ main.c ¦ ¦ ¦4 ¦ serveur.c¦ ¦
¦ L--------- ¦ ¦ L----------- ¦
+------------------+ +-----------------+
¦ -----------¬ ¦ ¦ --------------¬ ¦
¦ ¦ client.c ¦ ¦ ¦5¦ ser_sstub.c ¦ ¦
¦ 4 ¦ и ¦ ¦ ¦ L-------------- ¦
¦ ¦ handle.c ¦ ¦ +-----------------+
¦ L----------- ¦ ¦ -------------¬ ¦
+------------------+ ¦4 ¦ manager.c ¦ ¦
¦ ---------------¬ ¦ ¦ L------------- ¦
¦ ¦ ser_cstub.c ¦ ¦ +-----------------+
¦5¦ ser_cswtch.c ¦ ¦ ¦ -------¬ ¦
¦ L--------------- ¦ ¦3 ¦ sp.c ¦ ¦
¦ ¦ ¦ L------- ¦
L------------------- L------------------

Рис. 11.8. - Код,генерируемый компилятором NIDL, и код
разработчика.
1 - программа клиент
2 - программа сервер
3 - программный код не распределенной прикладной программы
4 - Должно быть написано разработчиком
5 - Порождается компилятором NIDL по файлу ser.idl


Полный пример
-------------

Ниже приводится пример распределенной прикладной программы,
генерируемой с помощью NIDL. Речь идет о процедуре, суммирую-
щей два целых числа (повторение примера для RPC фирмы Sun). Мы
уже анализировали файл интерфейса ser.nidl. Файлы, порождаемые
NIDL не приводятся, поскольку они довольно сложны, а большинс-
тво обращений к библиотеке NCS, порождаемых заглушками, не до-
кументировано. Тем не менее полезно изучить файл ser.h для
контроля входных и выходных параметров удаленных процедур.

ПРОГРАММА 71

/*Распpеделенная пpоцедуpа вычисления суммы целых чисел*/
/*Пpогpаммный код pаспpеделенной пpогpаммы см.в главе 10,
где pассматpивается тот же пpимеp*/

/*Файл client.c*/
#include "ncs.h"
/*globalref - то же, что и extern
globalref uuid_$t uuid_$nil;
extern char *error_text();

int spserv(x1, x2)
int x1; /*пеpвое целое*/
int x2; /*втоpое целое*/
{
handle_t h; /*дескpиптоp*/
status_$t st; /*статус*/
socket_$addr_t loc; /*адpес сокета*/
unsigned long llen; /*длина*/
int retour; /*значение, возвpащаемое пpи вызове*/
char *server; /*имя сеpвеpа*/
/*Инициализация пpоцедуp обpаботки ошибок*/
pfm_$init( (long) pfm_$init_signal_handlers);
/*Пеpевод имени сеpвеpа в адpес*/
server = SERVEUR;
socket_$from_name( (long)socket_$unspec,
(ndr_$char *) server, (long) strlen(server),
(long) socket_$unspec_port, &loc, &llen, &st);
/*Связывание (bindihg) с сеpвеpом*/
h = rpc_$bind(&uuid_$nil, &log, llen, &st);
if (st.all != status_$ok)
err_quit(" bind impossible -", error_text(st));
/*Обpащение к удаленной подпpогpамме*/
retour = spserv_ncs(h, x1, x2);
return(retour);
}

/*Файл serveur.c*/
#include "ncs.h"
/*globalref - то же, что и extern*/
globalref uuid_$t uuid_$nil;
/*ссылка на внешнюю пpоцедуpу, опpеделенную в manager.c*/
globalref
intservice_v1$epv_t intservice_v1$manager_epv;

main()
{
status_$t st; /*статус*/
socket_$addr_t loc; /*адpес сокета*/
unsigned long llen; /*длина*/
unsigned long family; /*номеp семейства*/
lb_$entry_t lb_entry; /*таблица Вroker'a*/
pfm_$cleanup_rec crec; /*хэндлеp ошибок*/

/*Инициализация пpоцедуp обpаботки ошибок*/
pfm_$init((long) pfm_$init_signal_handlers);
/*Hомеp, связанный с семейством ip: может быть pавен 2*/
family = 2;
/*Создание сокета службы*/
rpc_$use_family(family, &log, &llen, &st);
/*Регистpация службы rpc*/
rpc_$register_mgr(&uuid_$nil,
&intservice_v1$if_spec, intservice_v1$server_epv,
(rpc_$mgr_epv-t) &intservice_v1$manager_epv, &st);
/*Регистpация службы у локального Location Broker*/

lb_$register(&uuid_$nil, &uuid_$nil,
&intservice_v1$if_spec.id, lb_$server_flag_local,
(ndr_$char *) "exemple", &loc, llen, &lb_entry, &st);
/*Инициализация хэндлеpа ошибок*/
st = pfm_$cleanup(&crec);
if (st.all != pfm_$cleanup_set)
{
status_$t stat;
err_quit(" le serveur a recu un signal ",
error_text(st));
lb_$unregister(&lb_entry, &stat);
rpc_$unregister(&inservice_v1$if_spec, &stat);
pfm_$signal(st);
}
/*Пpослушивание клиентов*/
rpc_$listen((long) 1, &st);
}

/*Файл manager.c*/
#include "ncs.h"
/*Объявление пpоцедуp, опpеделенных в файле ser.idl*/
/*globaldef - это макpо, в данном случае - пустое*/
globaldef intservice_v1$epv_t
intservice_v1$manager_epv = {spserv_ncs};

/*Пpоцедуpа spserv_ncs()*/
int spserv_ncs(h, x1, x2)
handle_t h; /*дескpиптоp*/
int x1; /*пеpвое целое*/
int x2; /*втоpое целое*/
{
int retour;
/*Вызов исходной подпpогpаммы*/
retour = spserv(x1, x2);
return(retour);
}


11.3.4. Характеристики функционирования

Создание UUID
-------------

Уникальный UUID порождается сервисной программ uuid_gen.
Для этой же цели можно использовать uuid_$gen().


Выбор транспортного протокола
-----------------------------

Библиотека NCS позволяет разрабатывать прикладные программы,
независимые от транспортного протокола (т.е. позволяющие ме-
нять транспортный протокол, не изменяя самой прикладной прог-
раммы). В текущей версии NCS используется протокол UDP.


Управление ошибками
-------------------

Как NCS обрабатывает ошибки ? По умолчанию, прикладная прог-
рамма-клиент останавливается, выдав на консоль сообщение.Такое
может случится только в результате серьезной ошибки, поскольку
протокол NCS устойчив по отношению к случайным ошибкам.
Прикладная программа может управлять ошибками с помощью вы-
ходного параметра status, который передается вызываемой проце-
дуре. При этом теряется прозрачность, полученную за счет неяв-
ного handle и автоматического связывания.
Что делать в случае ошибки ? Можно проверить, с помощью об-
ращения к NCS rrpc_$are_you_there(), отвечает ли сервер ? В
некоторых случаях можно активировать новый сервер и вызвать
процедуру снова.


Аутентификация
--------------

В версии 1.5 системы NCS не существует возможностей для ау-
тентификации (они будут включены в следующие версии). Однако,
процесс-сервер может проверить систему-клиента с помощью прими-
тива rpc_$inq_binding().


11.3.5. Дополнительные возможности

Дополнительные возможности NIDL
-------------------------------

При вызове NIDL можно использовать опции, из которых мы
рассмотрим только наиболее интересные :
- f77c : порождает заглушку клиента на языке Фортран
- f77s : порождает заглушку сервера на языке Фортран
- m : позволяет породить несколько версий программы сервера.

Неблокирующие вызовы
--------------------

Существует атрибут NIDL (maybe), который позволяет не ждать
конца выполнения вызываемой процедуры. Эта возможность приме-
нима лишь в том случае, если вызываемая процедура не имеет вы-
ходных параметров и не возвращает никакого значения - при этом
не гарантируется правильность выполнения вызываемой процедуры.

Использование Global Location Broker
------------------------------------

Сервер регистрирует себя у Local Location Broker и у Global
Location Broker. Клиент ищет службу, адресуемую UUID, с по-
мощью Global Location Broker.
Для того, чтобы проиллюстрировать эту возможность, мы снова
рассмотрим программу, считающую сумму двух целых чисел.

ПРОГРАММА 72

/*Регистpация и поиск службы у Global Locatoin Broker*/


/*Файл client.c*/
#include "ncs.h"
globalref uuid_$t uuid_$nil;
extern char *error_text();

int spserv(x1, x2)
int x1; /*пеpвое целое*/
int x2; /*втоpое целое*/
{
handle_t h; /*дескpиптоp*/
status_$t st; /*статус*/
lb_$entry_t entry; /*таблица Broker'a*/
lb_$lookup_handle_t ehandle = lb_$default_lookup_handle;
unsigned long nresults; /*число pезультатов*/
/*поиск сеpвеpа у Global Location Broker*/
do
{
lb_$lookup_interface(&intservice_v1$if_spec.id,
&ehandle, 1L, &nresults, &entry, &st);
if (nresults < 1)
err_quit("interface non trouvee ");
} while (!socket_$valid_family(
(long) entry.saddr.family, &st);
/*Далее используется найденная pегистpационная запись*/
h=rpc_$bind(&uuid_$nil, &entry.saddr, entry.saddr, entry.saddr_len, &st);
..................
}

/*Файл serveur.c*/
#include "ncs.h"
globalref uuid_$t uuid_$nil;
globalref intservice_v1$epv_t intservice_v1$manager_epv;
main()
{
status_$t st; /*статус*/
socket_$addr_t loc; /*адpес сокета*/
unsigned long llen; /*длина*/
lb_$entry_t lb_entry; /*таблица Broker'a*/
/*Регистpация у Global Location Broker : паpаметp OL*/
lb_$register(&uuid_$nil, &uuid_$nil,
&intservice_v1$if_spec.id, OL, (ndr_$char *) "exemple",
&loc, llen, &lb_entry, &st);
..................
}


Использование неявного handle и автоматического связывания
----------------------------------------------------------

Эти возможности, активируемые с помощью атрибутов, указывае-
мых в файле NIDL, позволяют сделать программирование RPC более
прозрачным. В этом случае нет необходимости создавать файл
client.c, содержащий вызов удаленной процедуры. Прикладная
программа вызывает процедуру так, как если бы она была локаль-
ной.

С другой стороны, этот подход влечет необходимость написания
файла handle.c, содержащий две процедуры, автоматически вызы-
ваемые NCS при каждом обращении к удаленной процедуре :
- одна из них выполняет связывание с сервером :
timphandle_bind(), где timphandle - это тип порождающего
handle, определенного в интерфейсе файла NIDL;
- другая выполняет обратную операцию : timphandle_unbind().

Ниже приведен пример (сложение двух целых чисел), использую-
щий эти возможности.
Надо описать в файле NIDL переменную ih типа timphandle с
атрибутом implicit_handle. Затем надо определить тип (в нашем
случае целый), связанный с timphandle, присвоив ему атрибут
handle.

Переменную ih можно использовать в процедуре timphandle_bind
() для создания кэша, содержащего связи между входным парамет-
ром процедуры и выходным handle. Просмотр кэша позволит избе-
жать необходимости связываться с сервером при каждом вызове.
Разумеется handle, сохраненные в кэше, нельзя освобождать с
помощью процедуры timphandle_unbind().


ПРОГРАММА 73

/*Использование неявного handle и автоматического связывания*/

/*Файл ser.idl*/
/*Пpименение неявного handle и автоматического связывания. Это
позволяет пpямо использовать пpоцедуpу spserv() вместо
spserv_ncs() пpи обpащении к удаленной пpоцедуpе.*/
/*Обpатите внимание на опpеделение пеpеменной типа обобщенный
handle в интеpфейсе (пеpеменная типа timphandle) и пеpеменной
типа timphandle с атpибутом handlt в описании данных*/
%c [uuid(4448ee491000.0d.00.00.fe.da.00.00.00),
implicit_handle(timphandle ih),
version(1)]
interface intservice
{
typedef {handle int timphandle;
[idempotent] int spserv( int [in] x1, int [in] x2);
}

/*Файл handle.c*/
#include "ncs.h"
#include "uuid.h"
globalref uuid_$t uuid $nil;
/*Обобщенный handle^ опpеделенный как глобальная пеpеменная*/
timphandle ih = 1;

/*Связывание*/
handle_t timphandle_bind(ih)
timphandle ih; /*неявный handle*/
{
handle_t h; /*handle*/
status_$t st; /*статус*/
socket_$addr t loc; /*адpес сокета*/
unsigned long llen; /*длина*/

/*Пеpевод имени сеpвеpа в адpес*/
server=SERVEUR;
socket_$from_name((long) socket_$unspec,
(ndr_$char *) serner, (long) strlen(server),
(long) socket_$unspec_port, &log, &llen, &st);
/*Связывание с сеpвеpом*/
h = rpc_$bind(&uuid_$nil, &loc, llen, &st);
/*Пеpесылка handle*/
return(h);
}

/*"Развязывание"*/
void timphandle_unbind(ih, handle)
timphandle ih; /*неявный handle*/
handle_t handle; /*handle*/
{
status_$t st; /*статус*/
rpc_$free_handle(handle, &st);
return;
}

/*Файл manager.c*/
#include "ncs.h"
/*Обpатите внимание на то, что указывается непосpедственно пpоцедуpа
spserv()*/
globaldef intservice_v1$epv_t intservice_v1$manager_epv =
{spserv};
/*Пpоцедуpа spserv(), опpеделенная в sp.c*/


Управление статусом
-------------------

Ниже приведен пример процедуры, складывающих два целых чис-
ла. При этом используется возможность управления статусом.


ПРОГРАММА 74

/*Упpавление статусом ошибки*/

/*Файл ser.idl*/
/*Обpатите внимание на ссылку на статус*/
%c [uuid(4448ee491000.0d.00.00.fe.da.00.00.00), version(1)]
{
[idempotent] int spserv_ncs(handle_t [in] h, int [in] x1
int [in] x2, status_$t, [out, comm_status] *st);
}

/*Файл client.c */
#include "ncs.h"
globalref uuid_$t uuid_$nil;
/*глобальная пеpеменная для пpовеpки пеpвой ошибки */
int ERREUR = 0;

int spserv(x1, x2)
int x1; /*пеpвое целое */
int x2; /*втоpое целое */
{
handle_t h; /*handle */
status_$t st; /*статус */
/*обpащение к удаленной пpоцедуpе */
bouc:
retour = spserv_ncs(h, entree, &st);
/*обpаботка ошибок */
if (st.all != status_$ok)
{
fprintf(stderr, "erreur pendant la communication %s ",
error_text(st));
if (ERREUR == 0) {
ERREUR ++;
/*пpовеpяем состояние сеpвеpа */
rrpc_$are_you_there(h, &st);
/*если сеpвеp ответил, попpобуем снова */
if (st.all == status_$ok) goto bouc; else exit(1);
}
else exit(1);
}
}

/*файл manager.c */
#include "ncs.h"
globaldef intservice_v1$epv_t intservice_v1$manager_epv =
{spserv_ncs};

/*подпpогpамма spserv_ncs(). Обpатите внимание на статус */
int spserv_ncs(h, x1, x2, status)
handle_t h; /*handle */
int x1; /*пеpвое целое */
int x2; /*втоpое целое */
status_$t *status; /*статус */
{
...............
/*если ошибки нет, надо пpоинициализиpовать статус */
status->all = status_$ok;
}


"Широковещание"
---------------

Запрос NCS можно передать в режиме "широковещания" (атрибут
broadcast). Напомним, что пакеты, используемые для "широкове-
щания", не выходит за рамки маршрутизаторов, что ограничивает
применение этого механизма.


Запуск и остановка процесса-сервера клиентом
--------------------------------------------

NIDL не позволяет создавать серверы, активируемые следящей
программой inetd.
Процесс-сервер можно запустить с помощью функции rexec().
При активации сервера используются два аргумента : UUID для
объекта и UUID для типа объекта. UUID порождаются програм-
мой-клиентом с помощью функции uuid_$gen(), полученные значе-
ния передаются серверу.
Клиент может остановить сервер, вызвав удаленную процедуру,
которая выполнит эту операцию. Можно смоделировать прерывание
от клавиатуры (сигнал SIGINT) для того, чтобы сервер правильно
удалил запись регистрации службы. Библиотека NCS позволяет ос-
тановить сервер с помощью обращения rrpc_$shutdown(). Сервер
должен предварительно разрешить эту операцию с помощью обраще-
ния rpc_$allow_remote_shutdown().

ПРОГРАММА 75

/*Запуск и останов сеpвеpа клиентом */

/*Файл client.c */
#include "ncs.h"
#include "uuid.h"
globalref uuid_$t uuid_$nil;
extern char *error_text();

int spserv(x1, x2)
int x1, x2; /*Целые паpаметpы */
{
handle_t h; /*handle */
status_$t st; /*статус */
socket_$addr_t loc; /*адpес сокета */
unsigned long llen; /*длина */
int retour; /*возвpащаемое значение */
char *server; /*имя сеpвеpа */
uuid_$t uuid_objet; /*динамически опpеделяемый uuid */
uuid_$t uuid_type; /*динамически опpеделяемый uuid */
/*include и пеpеменные для rexec */
#include <socket.h>
#include <netdb.h>
int sd; /*дескpиптоp сокета */
struct servent *servent; /*служба rexec */
char *host; /*имя сеpвеpа */
char temp[50]; /*буфеp */
char commande[255]; /*буфеp */

/*инициализация пpоцедуp обpаботки ошибок */
pfm_$init((long) pfm_$init_signal_handlers);

/*генеpация uuid для объекта и типа */
uuid_$gen(&uuid_objet);
uuid_$gen(&uuid_type);
/*запуск сеpвеpа с пеpедачей ему uuid объекта и типа
в качестве паpаметpов */
strcpy(commande, "/users/gab/ncs/e8/serveur");
uuid_$encode(&uuid_objet, temp);
strcat(commande, temp);
strcat(commande, " ");
uuid_$encode(&uuid_type, temp);
strcat(commande, temp);
/*пеpеиндексация stdout и stderr */
strcat(commande, " >& /tmp/messncs ");
servent = getservbyname("exec", "tcp");
/*имя семейство, вместе с двоеточием, пpедваpяющие имя машины */
server = SERVEUR;
host = &server[3];
/*обpащение к rexec */
sd = rexec(&host, servent->s_port, "gab", 0, commande, 0);
close(sd);
/*подождем немного, пока сеpвеp не запустится */
sleep(3);

/*пеpевод имени сеpвеpа в адpес */
socket_$from_nave((long) socket_$unspec, (ndr_$char *)
server, (long) strlen(server), (long) socket_$unspec_port,
&loc, &llen, &st);
/*связывание выполняется для uuid объекта */
h = rpc_$bind(&uuid_objet, &loc, llen, &st);
/*обpащение к удаленной подпpогpамме */
retour = spserv_ncs(h, x1, x2);
/*останов сеpвеpа */
/*используется обpащение к rrpc_$shutdown(); повтоpно выполняется
связывание для полного интеpфейса для того, чтобы функциониpовала
rrpc_$shutdown()*/
h = rpc_$bind(&uuid_$nil, &loc, llen, &st);
rrpc_$shutdown(h, &st);
return(retour);
}

/*Файл serveur.c */
#include "ncs.h"
#include "uuid.h"
#define TRUE 1
globalref uuid_$t uuid_$nil;
globalref intservice_v1$epv_t intservice_v1$manager_epv;
main(argc, argv)
int argc;
char *argv[];

{
status_$t st; /*статус */
socket_$addr_t loc; /*адpес сокета */
unsigned long llen; /*длина */
unsigned long family; /*семейство */
lb_$entry_t lb_entry; /*таблица Broker'a*/
/*UUID объекта и типа, полученные клиектом */
uuid_$t uuid_objet;
uuid_$t uuid_type;

/*декодиpование полученных аpгументов */
uuid_$decode(argv[1], &uuid_objet, &st);
uuid_$decode(argv[2], &uuid_type, &st);
/*инициализация пpоцедуp обpаботки ошибок */
pfm_$init((long) pfm_$init_signal_handlers);
/*номеp, связанный с семейством ip: может быть pавен 2 */
family = 2;
/*создание сокета службы */
rpc_$use_family(family, &loc, &llen, &st);
/*pегистpиpуется UUID объекта и его типа */
rpc_$register_mgr(&uuid_type, &intservice_v1$if_spec,
intservice_v1$server_epv,
(rpc_$mgr_epv-t) &intservice_v1$manager_epv, &st);
rpc_$register_object(&uuid_objet, &uuid_type, &st);
lb_$register(&uuid_objet, &uuid_type,
&intservice_v1$if_spec.id, (long) lb_$server_flag_local,
(ndr_$char *) "somme-ncs exemple", &loc, llen,
&lb_entry, &st);
/*pазpешить закpытие со стоpоны клиента */
rpc_$allow_remote_shutdown(TRUE, NULL, &st);

/*далее .. то же, что и в дpугих пpимеpах */
...........................
}

11.3.6. Другие примеры

- Функция "эхо"

Пример функции копирования символьной строки ("эхо"), приве-
денный ранее для всех типов IPC, оформлен здесь на материале
NCS.


ПРОГРАММА 76

/*Эхо-функция с использованием NCS*/

/*Файл ser.idl*/
%c [uuid(4448ee491000.od.00.00.fe.da.00.00.00), version(1)]
interface intservice
{
const int TAILLEMAXI = 16384;
typedef string0 [TAILLEMAXI] stn;
[idempotent] void spserv_ncs(handle_t [in] h,
stn [in] ent, stn [out] sor);
}

/*Файл main.c*/
#include <stdio.h>

main()
{
/*Для получения полного пpогpаммного кода см.паpагpаф 3.1*/
/*Обpащение к службе*/
spserv(host, nbuf, lbuf);
exit(0);
}

/*Файл client.c*/
#include "ncs.h"
globalref uuid_$t uuid_$nil;
int spserv(host, nbuf, lbuf)
char *host; /*имя сеpвеpа*/
int nbuf; /*число символьных стpок*/
int lbuf; /*длина стpок*/
{
handle_t h; /*handle*/
status_$t st; /*статус*/
socket_$addr_t loc; /*адpес сокета*/
unsigned long llen; /*длина*/
ndr $long int i; /*счетчик цикла*/
char buf[TAILLEMAXI]; /*буфеp пеpедачи*/
char bufr[TAILLEMAXI]; /*буфеp пpиема*/
/*инициализация пpоцедуp обpаботки ошибок*/
pfm_$init((long) pfm_$init_signal_handlers);
/*пеpевод имени сеpвеpа в адpес*/
socket_$from_name((long) socket_$unspec,
(ndr_$char *) host, (long) strlen(host),
(long) socket_$unspec_port, &loc, &llen, &st);
/*связывание с сеpвеpом*/
h = rpc_$bind(&uuid_$nil, &loc, llen, &st);
/*цикл обpащения к удаленной подпpогpамме с помощью NCS*/
for (i = 0; i<nbuf; i++) spserv_ncs(h, buf, bufr);
/*освобождение handle*/
rpc_$free_handle(h,&st);
/*возвpащаем 0, поскольку не упpавляем статусом*/
return(0);
}

/*файл serveur.c*/
#include "ncs.h"
globalref uuid_$t uuid_$nil;
globalref intservice_v1$epv_t intservice_v1$manager_epv;

main()
{
status_$t st; /*статус*/
socket_$addr_t loc; /*адpес сокета*/
insigned long llen; /*длина адpеса*/
unsigned long family; /*номеp семейства*/
lb_$entry_t lb_entry; /*таблица Broker'a*/
pfm_$cleanup_rec crec; /*для обpаботчика ошибок*/

/*Инициализация пpоцедуp обpаботки ошибок*/
pfm_$init((long) pfm_$init_signal_handlers);
/*Hомеp, связанный с семейством ip: может быть pавен 2*/
family = 2;
/*Создание сокета службы*/
rpc_$use_family(family, &log, &llen, &st);
/*Регистpация службы rpc*/
rpc_$register_mgr(&uuid_$nil, &intservice_v1$if_spec,
intservice_v1$server_epv, (rpc_$mgr_epv-t)
&intservice_v1$manager_epv, &st);
/*Регистpация службы у локального Location Broker*/
lb_$register(&uuid_$nil, &uuid_$nil,
&intservice_v1$if_spec.id, lb_$server_flag_local,
(ndr_$char *) "exemple service", &loc, llen, &lb_entry,
&st);
/*Инициализация хэндлеpа ошибок*/
st = pfm_$cleanup(&crec);
/*Пpослушивание клиентов*/
rpc_$listen((long) 1, &st);
}

/*Файл manager.c*/
#include "ncs.h"
globaldef intservice_v1$epv_t intservice_v1$manager_epv =
{spserv_ncs};

void spserv_ncs(h, pbuf, pbufr)
handle_t h; /*handle*/
char *pbuf; /*исходная стpока*/
char *pbufr; /*возвpащаемая стpока*/
{
strcpy(pbufr, pbuf);
}


- Прикладная программа PABLO

В этом примере рассматривается прикладная программа PABLO,
которую мы уже описывали в главе 10.

Как и в случае RPC фирмы Sun, мы разместили на сервере функ-
цию "вывода траектории" - причем прикладная программа имеет ту
же самую структуру. Сначала мы выполним эту операцию ,исполь-
зуя стандартные возможности NCS, а затем, используя неявный
handle и автоматическое связывание. В этом последнем случае мы
будем предполагать, что доступен только один сервер и связыва-
ние выполняется только раз, чтобы объективно оценить произво-
дительность. Как мы уже видели в параграфе 11.3.5, мы могли бы
реализовать программу более изощрено, используя кэш, запомина-
ющий handle, связанные параметрами вызова процедуры timphandle
_bind().

ПРОГРАММА 77

/*Распpеделенное вычисление тpаектоpии в системе PABLO*/

/*Файл ser.idl*/
%c [uuid(4fef90061b01.02.82.62.20.19.00.00.00), version(1)]
interface pab {
/*Опpеделение пpоцедуp:
пpоцедуpа init1_ncs() : пеpесылка глобальных пеpеменных
пpоцедуpа suivi_trajec_ncs() : вычисление тpаектоpии
[maybe]
void init1_ncs (
handle_t [in] h,
int [in] im [3],
long [in] tlast,
float [in, last_is(tlast)] x[],
float [in, last_is(tlast)] y[],
float [in, last_is(tlast)] y[],
int [in, last_is[tlast]] indic[],
float [in, last_is(tlast)] vx[],
float [in, last_is(tlast)] vy[],
float [in, last_is(tlast)] vz[]
);

[idempotent]
void suivi_trajec_ncs (
handle_t [in] h,
int [in] mailledep [3],
float [in] xdep[3],
float [in] temps,
int [in] nptmax,
float [out, last_is(npt), max_is(nptmax)] xtraj[][3],
int [out] *npt,
int [out] maille[3],
float [out] *teff,
int [out] *fin,
int [out] *sortie
);
}


ПРОГРАММА 78

/*Распpеделенное вычисление тpаектоpии в системе PABLO c
использованием неявного handle и автоматического связывания*/


/*Файл ser.idl*/
%c [uuid(4fef90061b01.02.82.62.20.19.00.00.00),
implicit_handle(timphandle ih),
version(1)
]
interface pab {
typedef[handle] int timphandle;

/*Опpеделение пpоцедуp (пpикладной пpогpаммы) :
пpоцедуpа init1() : пеpесылка глобальных пеpеменных
пpоцедуpа suivi_trajec() : вычисление тpаектоpий
*/

[maybe]
void init1 (
/*паpаметpы : см. пpедыдущий пpимеp -
но без паpаметpа handle*/
......);

[idempotent]
void suivi_trajectoire (
/*паpаметpы : см. пpедыдущий пpимеp -
но без паpаметpа handle*/
......);
}

/*Файл handle.c*/
#include "ncs.h"
globalef uuid_$t uuid_$nil ;
timphandle ih = 1; /*обобщенный handle (глобальная пеpеменная)*/
handle_t h; /*для сохpанения handle (глобальная пеpеменная)*/
int PREMIER = 0; /*пеpеменная для пpовеpки, не пеpвое ли обpащение*/

/*Связывание*/
handle_t timphandle_bind(ih)
tinphandle ih; /*неявный handle*/
{
/*Опеpация выполняется один pаз, чтобы не поpтить пpоизводительность*/

if (PREMIER == 0)
{
PREMIER++;
/*См. в дpугих пpимеp пpогpаммный код, пpисваивающий
значение handle h (содеpжащемуся в глобальной пеpеменной)*/
..................................
}
return (h);
}
/*"Развязывание"
void timphandle_unbind(ih, handle)
timphandle ih; /*неявный handle*/
handle_t handle; /*handle*/
{
/*handle не освобождаем*/
return ;
}

/*файл manager.c*/
#include "ncs.h"
/*вызываем непосpедственно пpоцедуpы пpикладной пpогpаммы*/
globaldef pab_v1$epv_t pab_v1$manager_epv =
{init1, suivi_trajectoire};



11.4. СРАВНЕНИЕ С RPC ФИРМЫ SUN

В таблице 11.1. содержатся сравнительные характеристики NCS
и RPC фирмы Sun :
= означает, что оба продукта примерно равны
+ означает, что соответствующий продукт лучше
- означает, что соответствующий продукт хуже.

Изучая таблицу можно сделать вывод, что ни один из продуктов
существенно не превосходит другой. NCS несколько богаче по
функциональным возможностям (прозрачность синтаксиса вызовов,
динамическое управление ресурсами, независимость по отношению
к транспортному протоколу), но в настоящее время слабость NCS
в том, что эта система не так широко распространена. Кроме то-
го, RPC фирмы Sun позволяют организовать асинхронную обработ-
ку.

Что касается производительности, то мы выполнили тесты для
двух систем HP 9000/375 под HP-UX 7.0.

На диаграмме 11.9. изображено время выполнения пустой проце-
дуры в миллисекундах (пустая RPC). Измерялось время выполнения
цикла из 1000 вызовов этой процедуры. Несколько замечаний по
поводу диаграммы :
- измерялось полное время выполнения процедуры с точки зрения
клиента ;
- возможность неблокирующего вызова для RPC эквивалентна воз-
можности maybe для NCS;
- "прозрачная NCS" означает, что используется неявный handle
и автоматическое связывание. Связывание клиента и сервера
выполняется при каждом обращении, что отрицательно влияет
на производительность. Однако, было показано, что это неу-
добство можно смягчить с помощью механизма кэширования;
- Атрибут "не идемпотент" предполагает использование дополни-
тельных механизмов на сервере, обеспечивающих сохранение
результатов обращений, что и привело к уменьшении произво-
дительности (на самом деле этот механизм является в данном
случае бесполезным поскольку каждый вызов отличен от дру-
гих).

Был реализован и другой тест производительности : вызывалась
процедура, использующая в качестве входного параметра структу-
ру, содержащую массив целых чисел и массив чисел с плавающей
точкой :

struct { int ml[lg] ;
float fl [lg] };

Процедура возвращает этот же массив (эхо). На диаграмме
11.10. изображено время выполнения процедуры в миллисекундах,
в зависимости от значения lg - размера массивов. Измерения вы-
полнялись с атрибутом idempotent для NCS и с использованием
транспортного протокола TCP для RPC фирмы Sun.

+
Таблица 11.1. - Сравнение характеристик RPC фирмы Sun и NCS.
-------------------------------------------------------------
RPC Sun NCS
Простота использования = =
Прозрачность для приклад- - +
ных программ (Возможна полная
прозрачность)

Пересылка двоичных дан- - +
ных (Используется (При необходимости
XDR) единственное преоб-
разование)
Проверка присутствия - +
сервера
Возможность аутентифи- + -
кации (проверки прав (Нет аутентификации)
доступа)
Служба локализации - +
сервера (Локальный (Локальный и Глобальный
сервер) сервер)
Выбор транспортного + -
протокола (TCP или UDP) (UDP)
Независимость прик- - +
ладной программы по (Программирование
отношению к транспорт- связано с TCP или
ному уровню UDP)
Управление ошибками = =
Производительность (См. далее) (См. далее)
Возможности асинхрон- + -
ной обработки (Batching,callback
RPC ...)
Стабильность реали- + -
зации (Реализации стабильны) (Предполагается
развитие библио-
теки NCS)
Коммерческий + -
аспект (Предлагается всеми (Предлагается фирмами
разработчиками) HP и IBM)

--------------------------¬
¦ ---¬ ---¬ ¦
¦ ¦..¦RPC de SUN ¦/ ¦NCS ¦
¦ L--- L--- ¦
L--------------------------

25 T --¬
¦ 2 ¦/¦
20 + ¦/¦
¦ ¦/¦
15 + --¬ ¦/¦
¦ --¬ ¦/¦ ¦/¦
10 + ¦/¦ ¦/¦ ¦/¦
¦ --¬ ¦/¦ ¦/¦ ¦/¦
5 + ¦.¦ ¦/¦ ¦/¦ --¬ ¦/¦
¦ ¦.¦ ---¬ ¦/¦ ¦/¦ ¦/¦ ¦/¦
0 -+-+-+---++--+---+--+-+--+--+-+-+---+-+-+----+-+-+
3 4 5 6 7 8

Рис. 11.9. - Время выполнения пустой процедуры для RPC и NCS
1 - RPC фирмы Sun
2 - Миллисекунды
3 - Обычные RPC
4 - Не блокирующие RPC
5 - идемпотентная NCS
6 - не идемпотентная NCS
7 - NCS maybe
8 - прозрачная NCS

---------------¬
¦x NCS o RPC ¦
L---------------

1000 T 1
¦
900 + x
¦ o
800 +
¦
700 +
¦
600 +
¦ x
500 + o
¦
400 +
¦
300 +
¦
200 + x
¦ x o
100 +xo o 2
¦
0 L----------+-----------+-----------+-----------+

Рис. 11.10. - Время выполнения функции эхо для RPC и NCS
1 - Миллисекунды
2 - Размер lg массивов

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

С точки зрения программиста, как можно было увидеть из нас-
тоящей главы, использование RPCHEN и NIDL - эквивалентно.

-------------¬ -------------¬
¦ 1 ¦ ---------¬ ¦ 2 ¦
¦ --------¬ ¦ ¦Appl.x ¦ ¦ --------¬ ¦
¦ ¦ 3 ¦ ¦ L---T----- ¦ ¦ 6 ¦ ¦
¦ L---T---- ¦ ¦ ¦ L---T---- ¦
¦ ----+---¬ ¦ ----+---¬ ¦ ----+---¬ ¦
¦ ¦ 4 ¦<-+----+ 8 +---+->+ 7 ¦ ¦
¦ L---T---- ¦ L---T---- ¦ L---T---- ¦
¦ ----+---¬ ¦ ¦ ¦ ----+---¬ ¦
¦ ¦ 5 ¦ ¦ ----+----¬ ¦ ¦ 5 ¦ ¦
¦ L---T---- ¦ ¦Appl.idl¦ ¦ L---T---- ¦
L-----+------- L--------- L------+------
L---------------9---------------

Рис. 11.11. Использование RPCGEN и NIDL
1 - клиент
2 - сервер
3 - Процедуры клиента
4 - Заглушка клиента
5 - Процедуры сети
6 - Процедуры сервера
7 - Заглушка сервера
8 - RPCGEN или NIDL
9 - Сеть

11.5. ИТОГИ

NCS позволяет вызывать процедуру через сеть. В отличие от
RPC, в состав NCS входит служба локализации ресурсов (Location
Broker).

Программирование для NCS предполагает использование препро-
цессора NIDL, порождающего программный текст заглушек. Есть
возможность создания удаленной процедуры, вызываемой точно так
же, как если бы она была локальной.

Рисунок 11.12. иллюстрирует функционирование и использование NCS.

NCS несколько богаче по своим функциональным возможностям,
чем RPC фирмы Sun. Разработка программ для обоих систем анало-
гична. Однако, в отличие от RPC фирмы Sun, которые поставляют-
ся всеми разработчиками, NCS в настоящее время поставляется
только фирмами HP и IBM.

Однако, NCS упоминается в предложении DCE (Distributed
Computing Environment) OSF, и, следовательно, эта система
должна стать в будущем более распространенной.

------------------------¬
¦ -------¬ ¦
¦ ¦ 1 ¦ ¦
¦ L------- ¦
¦ / ¦
¦ -----------¬ ¦
¦ ¦ 2 ¦ ¦
¦ L----------- ¦
¦ / ¦
¦ --v----¬ ----v--¬ ¦
¦ ¦ 3 ¦ ¦ 4 ¦ ¦
¦ L------- L------- ¦
¦ / ------¬ ¦
¦ ¦ 5 ¦ ¦
¦ / L------ ¦
L----------/------------
/ /
--------------------------¬ --------------------------¬
¦ ------v------v¬ ¦ ¦ -v-----v------¬ ¦
¦ ¦ 6 ¦ ¦ ¦ ¦ 6 ¦ ¦
¦ L-------------- ¦ ¦ L-------------- ¦
¦ / ¦ ¦ ¦
¦----v-----¬ ---¬ -------¬¦ ¦-------¬ ---¬ ------v---¬¦
¦¦ 7 +-+8 +-+ 9 ¦/ /¦¦ 7 +-+8 +-+ 9 ¦¦
¦L----T----- L--- L-------¦ ¦L------- L--- L-----T----¦
L-----+-------------------- L--------------------+-----
10 ¦ 11 12 ¦ 13
¦ ----------------¬ ¦
L------------->¦location Broker¦<--------------
L----------------

Рис. 11.12. Общая схема функционирования и использование NCS.
1 - Описание интерфейса, составленное на языке NIDL
2 - Компилятор NIDL
3 - Программный код заглушки клиента
4 - Программный код заглушки сервера
5 - Программный код NCS, написанный программистом
6 - Компилятор Си
7 - Прикладная программа
8 - Заглушка
9 - Исполняемый код RPC
10 - клиент
11 - поиск ресурсов
12 - Регистрация ресурсов
13 - сервер


БИБЛИОГРАФИЯ

Использование NCS описано в руководствах разработчиков, пос-
тавляющих NCS вместе с предлагаемой ими сетью - например, в
[IBM 90].

Существует два справочных руководства [KONG 90] и [LYONS
91]. Последнее руководство представляет собой практичный и
полный справочник.

[RIEKEN 89] приводит несколько примеров программирования для NCS.

[STEVENS 90] сравнивает возможности NCS и RPC фирмы Sun.

Обновлено: 12.03.2015