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

1 ... 90 91 92 [ 93 ] 94 95 96 ... 98


потоков в процессе. Ожидается, что такие возможности предоставят и другие фирмы, которые поддерживают в своих системах многопотоковые программные среды.

13.8. Пример распределенного

многопотокового приложения

в этом разделе рассматривается многопотоковая распределенная программа. Это интерактивная профамма, которая выполняет пользовательские команды shell на любой машине в локальной сети. Для связи с выделенными RPC-серверами, работающими на удаленных хост-машинах, в профамме используются удаленные вызовы процедур. Когда пользователь вводит команду shell, программа создает поток для установления соединения с RPC-сервером, работающим на указанной пользователем хост-машине. Этот сервер выполняет пользовательскую команду на указанной машине, а поток проверяет его код завершения и в случае ошибки выставляет пользователю соответствующий флаг.

Используя отдельный поток для обработки каждой пользовательской команды, программа может принимать новую команду, продолжая выполнять одну или несколько предыдущих команд другими потоками. Таким образом, для ввода команды пользователям не приходится ждать, пока завершится выполнение предьщущей команды. Благодаря этому такая программа более интерактивна , чем соответствующая однопотоковая версия. К тому же программа может свободно использовать все ресурсы, имеющиеся на многопроцессорной машине.

Используя RPC, программа может распределить рабочую нагрузку на несколько сетевых компьютеров, благодаря чему значительно повышается ее производительность и гибкость. Используя метод широковещательных RPC, программа автоматически определяет, на каких хост-машинах работают RPC-серверы. Таким образом, от пользователя требуется лишь зафузить RPC-серверы на выбранные им машины (это могут быть и гетерогенные платформы, например рабочие UNIX-станции, VMS-мащины и машины с Windows NT) и запустить программу на хост-машине, которая поддерживает многопотоковые профаммы (например, на рабочей станции Sun с ОС Solaris).

RPC-сервер, который взаимодействует с этой интерактивной программой и выполняет пользовательские команды shell, содержится в профамме shell svc.C. В этой программе используются классы RPC, определенные в главе 12. Они позволяют создать объект RPCjsvc для работы RPC. Ниже приведен файл shelljsvc. С:

tinclude mshell.h ♦include RPC.h

RPC svc *sycp;

jj. указатель на объект RPC-сервера

/* RPC-функция для выполнения одной пользовательской команды */

int execshell( SVCXPRT* xtrp )

static int res=0, rc= RPC SUCCESS; char *cmd = 0;

/* получить команду shell от RPC-клиента */ if (svcp->getargs( xtrp, (xdrproc t)xdr string, (caddr t)scmd)!=RPC SUCCESS)

return -1;

/* выполнить команду с помощью функции system */ res = system(cmd);

/* послать результат выполнения RPC-клиенту */ if (svcp->reply(xtrp, (xdrproc t)xdr int, (caddr t)sres)!=RPC SUCCESS) rc = -2; ~

return rc;

int main(int argc, char* argv[]) (

/* создать объект RPC-сервера для функции execshell */ RPC svc *svcp = new RPC svc( SHELLPROG, SHELLVER,

argc==2 ? argv[l] : netpath ); if (Isvcp II !svcp->good()) return 1; д

/* ждать запросы RPC-клиентов */

if (svcp->run func( EXECSHELL, execshe.ll )) return 3# j return 0; /* shouldnt get here */ .ч*-

Программа RPC-сервера создает объект RPC svc для выполнения футгк-ции execshell по запросу RPC-клиента. Функция execshell принимает от клиента символьную строку, которая заверщается символом NULL и содержит команду sliell ОС UNIX. Для выполнения этой команды вызывается функция system. Значение, возвращенное вызовом system, передается RPC-клиенту (функцией RPC svc::reply). Серверный RPC-процесс - это демон, который работает в фоновом режиме до тех пор, пока система не закроется или пока он не будет явно уничтожен пользователем.

Заголовок RPC.h и файл ЛРС. С рассмотрены в главе 12 (раздел 12.5). В следующем ниже заголовке mshell.h заданы номер профаммы, номер версии и номер процедуры для функции execshell. Кроме этого в заголовке определен класс shellObj, назначение которого поясняется ниже: /*

* Please do not edit this file. *It was generated using rpcgen.

fifndef SHELL H RPCGEN fdefine SHELL H RPCGEN finclude <iostream.h>



♦ include <rpc/rpc.h> . -

finclude <thread.h> к

finclude <string.h>

fdefine SHELLPROG ((unsigned long) (0x20000001))

fdefine SHELLVER ((unsigned long) (1))

fdefine EXECSHELL ((unsigned long) (1))

typedef void* (*FNPTR) ( void *) ;

class shellObj (

char* help msg; FNPTR action; void* (*fnptr)( void* ); int create thread; public:

/* конструктор. Задает сообщение-подсказку, функцию action

и значение errno */ ShellObj( const char* msg, FNPTR func, int thr ok=l) (

help msg = new char[strlen(msg)+1];

strcpy(help msg,msg);

action = func;

create thread = thr ok;

функция-деструктор

-shellObjО { delete help msg; };

©streams usage( ©streams os, int idx) {

OS idx : help msg endl; return os;

выполнить функцию action с помощью потока int doit( void* argp ) ( , if (create thread) ( thread t tid;

if (thr create(0,0,action,argp,THR D3TACHED,Stid)) perror( thr create );

return (int)tid;

! else return (int) action (argp); fendif /* ! SHELL H RPCGEN */

Программа RPC-сервера компилируется и запускается на любом компьютере, где поддерживаются RPC-функции стиля UNIX System V.4:

% cc -DSYSV4 shell svc.C RPC.С -о shell svc -Insl % shell svc s

Главная программа - main shell.C. Это управляемая в режиме меню программа, которая многократно отображает на устройстве стандартного

вывода меню вариантов. Пользователь выбирает вариант, используя индексы меню, и программа вьшолняет этот вариант в отдельном потоке. Программа mainshell. С приведена ниже:

finclude <strstream.h> tinclude <thread.h> tinclude <string.h> tinclude <stdio.h> tinclude mshell.h

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

серверные процессы */ extern void* collect hosts( void* argp ); extern void* display hosts( void* argp ); extern void* exec shell( void* argp );

ShellObj *funcList[4]; диспетчерская таблица

int numFunc = sizeof(funcList)/sizeof(funcList[0]); rwlock t rwlck;

/* выход из программы */ void* quit prog( void* argp ) (

return (void*)0;

/* выполнить на хост-мащине одну команду shell */

void* getcmd( void* argp )

char host[20}, cmd[256}, cmd2[256}; cout shell cmd> flush; cin cmd;

cin.getline(cmd2,256); cout host: flush; cin >> host; if (!cin)

cout Inavlid input\n flush; else (

if (strlen(cmd2)) strcat(strcat(cmd, ),cmd2); strcat(strcat(cmd, / ),host); char* ptr = new char[strlen(cmd)+1}; ostrstream(ptr,strlen(cmd)+1) cmd; if (thr create(0,0,exec shell,ptr,THR DETACHED,0)) perror( thr create );

return (void*)l;

/* прочитать выбранный пользователем вариант и выполнить его */

int exec cmd()

char buf[256};

cout Selection> flush; cin buf;



cout endl; if (cin) {

int idx = -1;

istrstream(buf) idx;

if (idx >=0 && idx < numFunc)

return funcList[idx]->doit(0);

else cerr Invalid inputXn ;

return 0;

/* вывести меню */

void display menu(ostreams os)

for (int i=0; i < numFunc; i++)

(void)funcList[i]->usage(os,i);

/* инициализация блокировки чтения-записи и диспетчерской таблицы */ int init alio

if (rwloclc init (Srwlc)c, USyNC THREAD, 0)) { perror( rwlock init ); return -1;

funcList[0] = new shellObj ( Collect host names , collect hosts); funcList[l] = new shellObj ( Display host names , display hosts) ; funcList [2] = new shellObj ( Execute a shell command , getcmd, 0); funcList[3] = new shellObj ( Quit , quit prog, 0); return 0;

/* главный модуль */ int main () {

if (init all() == 0) do {

display menu(cerr);

if (!exec cmd()) break; } while (1); thr exit(0); return 0;

В этой интерактивной программе пользователю предлагается меню из четырех пунктов:

Индекс Функция Назначение

0 collect hosts Находит все хост-машины, на которых работает RPC-

сервер

1 display hosts Выводит имена всех хост-машин, на которых работает

RPC-сервер

2 getcmd Выполняет команду shell

3 quit prog Выход из интерактивной профаммы

Адреса этих функций хранятся в диспетчерской таблице funcList. Когда пользователь вводит номер варианта, это целое значение используется как индекс в диспетчерской таблице для вызова соответствуюшей функции. Например, если пользователь введет О, то будет вызвана функция collect hosts. Каждый элемент таблицы funcList - это указатель на объект shellObj. Класс shellObj определяется в заголовке mshell.h. Каждый объект shellObj содержит справочное сообшение, поясняюшее его использование, указатель на определяемую пользователем функцию (которая может вызываться для выполнения фактической задачи объекта) и флага, который показывает, нужно ли вызывать эту функцию через поток. Создаются четыре объекта shellObj для четырех функций - collecthosts, display hosts, getcmd, quit prog. Они обозначаются с использованием элементов О, 1, 2 и 3 таблицы funcList.

Функция main нашей интерактивной программы сначала вызывает функцию init all, которая инициализирует блокировку чтения-записи rwlck и диспетчерскую таблицу funcList с четырьмя объектами shellObj. После этого главная функция входит в цикл, вызывая функцию displayjnenu для вывода меню на экран. Затем вызывается функция execcmd, которая приглашает пользователя выбрать элемент меню и вызывает функцию, соответствуюшую этому элементу. Цикл завершается, когда exec cmd возврашает нулевое значение, и функция main завершается через функцию thr exit.

Функция displayjnenu просматривает диспетчерскую таблицу funcList и вызывает функцию shellObj:.usage для каждого объекта shellObj, обозначенного элементом таблицы. В результате каждый объект shellObj направляет в поток вывода cerr информацию о своем использовании и соответствующий ему индекс диспетчерской таблицы.

Вариант, выбранный пользователем в меню, читается функцией exec jmd. Эта функция следит за тем, чтобы индекс выбранного пользователем элемента лежал в диапазоне 0-3; в противном случае она выставляет флаг ошибки. Если пользователь выбрал допустимый вариант, функция exec cmd использует эту цифру как индекс таблицы funcList для получения указателя на объект shellObj. Затем она вызывает функцию shellObj::doit этого объекта. Функция execjmd возврашает нулевое значение, если не может получить данные от пользователя, либо значение, возвращенное функцией shellObj::doit.

Функция execjmd передает каждой вызываемой ею функции shellObj:.doit нулевое значение аргумента. Для элементов меню О и 1 соответствующие функции {collectjiosts и displayjiosts) выполняются отсоединенными потоками немедленно. Для элементов 2 и 3 соответствующие функции {getcmd и quitj>rog) отсоединенными потоками не вьшолняются. Каждая из четырех функций возвращает ненулевое значение, если она выполнена успешно, или О, если она завершилась неудачно.

Потоки, созданные ддя выполнения функциями collectjiosts и display hosts, отсоединяются от главных потоков. Как только они завершатся, их ресурсы и идентификаторы могут использоваться новыми потоками. Это объясняется тем, что интерактивная программа может работать довольно долго и должна на соответствующем уровне поддерживать скорость своей реакции на действия



1 ... 90 91 92 [ 93 ] 94 95 96 ... 98

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