Программирование >>  Структура ядра и системные вызовы 

1 ... 79 80 81 [ 82 ] 83 84 85 ... 98


/* зарегистрировать RPC-функцию и ожидать RPC-вызовы */ if (svcp->run func( ACLKPROC, set alarm )) return 1; /

return 1; /* сюда серверный процесс не должен попасть никогда V/

Серверная программа начинает работу с создания объекта RPC svc для инициирования RPC-функции setjalarm. Номера программы, версии и процедуры этой RPC-функции - ACLKPROG, ACLKVER и ACLKPROC соответственно. Вызвав функцию RPC svc::run, сервер ожидает поступления клиентских RPC-запросов.

Получив клиентский RPC-запрос, сервер вызывает RPC-функцию setjalarm, которая, в свою очередь, активизирует функцию RPC svc::getargs для извлечения информации об обратном вызове RPC-функции. Эта информация хранится в переменной argRec. После успешного выполнения вызова RPC svc::getargs сервер активизирует функцию RPC jvc::reply, которая возвращает клиенту полученные значения. Обратный вызов завершается, и клиент может перейти к выполнению других операций.

После вызова RPCjsvc::reply сервер порождает дочерний процесс для обслуживания данного клиента, а родительский процесс возвращается в цикл опроса и ожидает поступления других RPC-запросов.

Порожденный процесс вызьшает API alarm для настройки сигнала SIGALRM, который должен посьгааться ему по истечении установленного клиентом интервала времени. Он также вызывает API signal, который пере-хватьтает сигнал SIGALRM. Наконец, порожденный процесс вызывает API pause, который приостанавливает выполнение этого процесса до получения сигнала SIGALRM.

Когда в порожденный процесс доставляется сигнал SIGALRM, вызьта-ется функция calljclient. Эта функция настраивает объект RPCjcls на работу с функцией обратного вызова клиента и посылает клиентской RPC-функции в качестве аргумента значение, равное времени, которое осталось до сигнала будильника (должно быть равно нулю). После завершения RPC-вызова эта функция активизирует функцию exit, которая завершает порожденный процесс.

Программа-клиент в нашем примере называется aclkjcls. С:

♦include <netconfig.h> ♦include aclock.h ♦include RPC.h

RPC svc *svcp = 0;

♦define CLNTPROGNOM 0x20000105

/* функция обратного RPC-вызова клиента */

int callback( SVCXPRT* xtrp )

u long timv;

/* получить значение времени, оставшегося до сигнала будильника */ if (svcp->getargs(xtrp, (xdrproc t)xdr u long, (caddr t)Stimv)!=RPC SUCCESS)

cerr client: get alarm time fails\n , return -1;

cerr client: alarm time left is: timv endl;

/* возвратить серверу полученные значения */

if (svGp->reply(xtrp, (xdrproc t)xdr void, 0)!=RPC SUCCESS)

cerr client: send reply failed\n ; return -2;

/* выполнять другую работу, затем завершить клиентский процесс */ exit(O);

/* зарегистрировать обратный вызов на RPC-сервере */

int register callback( char* local host, char* svc host, u long alarm time)

struct arg rec argRec;

/* сообщить удаленному серверу хост-имя процесса, номер программы, номер версии, номер процедуры и интервал времени до сигнала будильника */

argRec.hostname = local host;

argRec.prognum = svcp->progno(>;

argRec.versnum = CLNTVERNOM;

argRec.procnum = CLNTPROCNUM;

argRec.atime = alarm time;

/* настроить клиентский процесс на соединение с RPC-сервером */ RPC cls clnt( svc host, ACLKPROG, ACLKVER, netpath ); if (!clnt.good()) return 1;

/* вызвать RPC-функцию сервера (set alarm) */

if (clnt.calK ACLKPROC, (xdrproc t)xdr arg rec, (caddr t)sargRec, (xdrproc t)xdr void, (caddr t)0 ) !=RPC SUCCESS) {

return 2;

cerr client: getpidO : RPC called done\n ; return 0;

/* клиентская главная функция */ int main (int argc, char* argv[}) (

if (argc!=4} (

cerr usage: argv[0) <local-host> <svc-host>

<transport>\n ;

return 1;

/* создать серверный процесс для приема обратного вызова от удаленного сервера */

if (!(svcp= new RPC svc( RPC ANYFD, argv[3], 0, CLNTVERNOM ))) return 2;



/* определить функцию обратного вызова */ \в

svcp->add proc( CLNTPROCNUM, callback ); определить функцию/

обратного вызова

/* зарегистрировать обратный вызов на удаленном сервере */ if (register callback( argvtl), argv[2], 10)) return 3;

/* выполнить другую работу ... */

svcp->run(); /* ожидать истечения заданного интерваа времени

до сигнала будильника */ return 0; I

Клиентский процесс начинает работу созданием объекта RPCjsvc для регистрации RPC-функции обратного вызова callback с помощью демона rpcbind (через API svcjcreate). Номера программы, версии и процедуры функции обратного вызова - CLNTPROGNUM, CLNTVERNUM и CLNTPROCNUM соответственно. После создания объекта RPCjsvc клиент вызывает функцию register callback, для того чтобы сообщить серверу заданный временной интерват и данные обратного вызова. После выполнения функции registerjcallback клиент Переходит к выполнению другой работы. Затем он активизирует функцию RPCsvcr.run и ожидает обратного вызова.

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

Клиентская RPC-функция callback вызывается сервером будильника по истечении указанного времени. Функция callback вызывает RPCjsvcr.getargs, чтобы извлечь аргумент сервера (время, оставщееся до сигнала будильника). Затем она возвращает серверу полученные значения (через RPC svc::reply). Наконец, функция вызывает exit и завершает таким образом клиентский процесс.

Функции XDR-преобразования этой программы содержатся в ack xdr.c:

* Please do not edit this file.

* It was generated using rpcgen. */

♦include aclock.h bool t

xdr name t(register XDR *xdrs, name t *objp) (

register long *buf;

if (!xdr string(xdrs, objp, MAXNLEN)) return (FALSE);

return (TRUE);

bool t

xdr arg rec(register XDR *xdrs, arg rec *objp) (

register long *buf;

if (!xdr name t(xdrs, if (!xdr u long(xdrs, if (!xdr u long(xdrs, if (!xdr u long(xdrs, if (!xdr u long(xdrs, return (TRUE);

&objp->hostname)) return (FALSE) &objp->prognum)) return (FALSE) &objp->versnum)) return (FALSE) &objp->procnum)) return (FALSE) &objp->atime)) return (FALSE);

Эти XDR-функции преобразуют информацию для обратного вызова клиента, указанную в записи struct arg rec, которая посылается серверу в RPC-вызове setjlme.

Ниже показано, как компилирзтотся и запускаются рассмотренные программы. Предполагается, что серверный процесс работает на машине saturn, а клиентский - на машине fruit.

На машине saturn:

saturn% СС -DSYSV4 -о aclk SYC aclk svc.C RPC.C aclk xdr.c \

-Isocket -Insl saturn% aclk svc &

Ha машине fruit. fruit%

fruit%

CC -DSYSV4 -o aclk cls aclk cls.C RPC.C aclk xdr.c \ -Isocket -Insl

aclk cls fruit saturn netpath

12.10. Временный номер RPC-программы

в приведенном выше примере у клиентской RPC-функции заранее определены номера программы, версии и процедуры, что не позволяет запускать в сети одновременно несколько клиентских процессов. Эту проблему можно преодолеть, создав разные версии клиентской RPC-программы, каждая из которых имеет свой номер. Однако в этом случае значительно усложняется сопровождение программ. Более эффективное решение - сгенерировать для каждого клиентского процесса на время его выполнения номер RPC-программы (временный номер). Тогда в локальной сети одновременно сможет работать множество клиентских процессов (сервер различит их по уникатьным номерам RPC-программ). Однако это касается версии RPC, реализованной в ОС UNIX System V.4. Метод генерации временных номеров портов поддерживают не все UNIX-системы.

Номера RPC-программ с 0x40000000 по OxSFFFFFFF зарезервированы для использования в качестве временных номеров. Это позволяет любому



процессу динамически связываться с демоном rpcbind и резервировать в качестве номера (или номеров) RPC-программ одно или несколько этих/ значений при условии, что номер не задействован другими процессами. Временные номера RPC-программ, затребованные процессом, освобождаются после его завершения и могут быть использованы другими процессами.

Временный номер RPC-программы освобождается с помошью статической функции RPC svc::gen progNum. Эта функция вызывает API rpcb set ддя каждого номера из диапазона 0x40000000 - OxSFFFFFFF и спрашивает у демона rpcbind, присвоен ли тот или иной номер какому-либо процессу. Функция находит младший из свободных временных номеров, и rpcb set регистрирует этот номер и указанный номер версии в rpcbind.

API rpcbjset имеет следующий прототип:

#inciude <rpc/rpc.h>

boot t rpcbjset (const u long prognum, const u long versnum,

const struct netconfig* netconf, const struct netbuf* addr);

Аргументы prognum и versnum - это затребованные номера RPC-программы и версии, которые должны быть присвоены вызывающему процессу. Аргумент netconf содержит информацию о способе передачи данных, используемом вызывающим процессом. Аргумент addr - это сетевой адрес вызывающего процесса.

При успешном выполнении эта функция возвращает значение TRUE, и затребованные номера программы и версии регистрируются в rpcbind для процесса с указанным адресом и транспортным протоколом. В случае неудачи функция возвращает значение FALSE.

Аргументы netconf и addr функции RPC svc::gen progNum получить не так просто, особенно если обработчик сервера создан посредством API svc create. Если же для этого был использован API svc tli create, то доступ к значениям netconf и addr сервера осуществить легко.

Чтобы использовать переходные номера программ, в класс RPCcls можно добавить следующую перефуженную функцию-конструктор RPC svc::RPC svc:

RPC svc( int fd, char* transport, u long progno, u long versno)

rc = 0; /* статус неудачного заверщения */

struct netconfig* nconf = getnetconfigent(transport); if (!nconf) {

cerr invalid transport: transport << endl;

return;

/* создать обработчик сервера */

SVCXPRT *,xprt .= svc tli create(fd, nconf, 0, 0, 0);

if (!xprt) {

cerr create server handle failsXn ; return;

if (Iprogno) { /* сгенерировать переходный номер */

progno * gen progNum{versno, nconf, &xprt->xp ltaddr); nconf = 0; /* дать svc reg указание не общаться

с rpcbind */

if (svc reg(xprt, progno, versno, dispath, nconf)==FALSE)

cerr register prognum failedXn ; else (

prognum = progno; vernum = verno; rc = 1;

freenetconfigent { nconf )..

В этой перегруженной функции-конструкторе вызывается функция getnetconfigent, которая возвращает указатель на запись данных типа struct netconf с информацией о способе передачи данных по сети для данного аргумента функции. После того как этот указатель получен (он содержится в переменной nconf), вызывается API svc tli create, который создает обработчик сервера и возвращает указатель на него. Переменная netconf освобождается с помощью функции freenetconfigent.

Функция svcjlicreate имеет следующий прототип:

#include <rpc/rpc.h>

SVCXPR* svc tff create (const Int fd, const struct netconfig* netconf,

const struct t blnd* baddr, const u lnt sendsz, const u int recsz);

Аргумент fd - это дескригттор файла, обозначающий файл устройства, связанный с выбранным механизмом транспортировки. Если его значение задано как RPCANYFD, то используется файл устройства, определенный аргументом netconf. Адрес, присвоенный серверу, определяется аргументом baddr, если его значение не NULL; в противном случае используется адрес, принятый по умолчанию для данного транспортного протокола. Аргументы sendsz и recvsz задают необходимые размеры буферов приема и передачи для обработчика сервера. Если их значения равны нулю, то используются стандартные для фанспортного протокола размеры.

В случае неудачи API svcjtijcreate возвращает NULL; в противном случае возвращается указатель на обработчик сервера.

Если функция svcjlicreate выполняется успешно и заданное значение аргумента progno равно нулю, вызывается функция RPC svc::genj)rogNum,



1 ... 79 80 81 [ 82 ] 83 84 85 ... 98

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика