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

1 ... 81 82 83 [ 84 ] 85 86 87 ... 98


char pathnm[256] ; f-:

sprintf (pathnm, %s/%s , darg->dir name,d->d rvame); if {Istat(pathnm,sstatv)) (

nl->uid = statv.st uid;

nl->modtime = statv.st mtime;

*nlp = 0; res.errno = 0; closedir(dirp);

/* передать список файлов каталога клиенту */

if (svcp->reply(xtrp, (xdrproc t)xdr res, (caddr t]&res]! RPC SUCCESS) return -2;

worJ? in progress 0; /* процесс может быть уничтожен сигналом

будильника */

return RPC SUCCESS;

/* программа обработки сигналов */ void done( int sig )

if (worlc in progress] (

/* re-schedule time-out */

signaK SIGALRM, (void(*) (int]) done );

alarm( ttl ) ;

char msg[ 1024 ]; time t tim = time(O);

sprintf( msg, svc (%d] exiting: %s , getpid(], ctime(Stim) ];

write ( 1, msg, strlen(msg] ];

exit(O);

I &

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

char msg[ 1024 ];

FILE *fp = fopen( /dev/console , w );

fprintf( fp, sv3: argc=%d, argv{l]=%s\n , argc, argc > 1 ?

argvfl] : Nil );

if (argc > 1 s& sscanf(argv{l], %d ,sttl]!=1) {

cerr Invalid argument: argv{l) endl; return 1;

switch (forkO) {

case 0: break;

case -1: perror( fork );

default: return errno;

/* закрыть все потоки ввода-вывода, кроме дескриптора О */

struct rlimit rls;

rls.rlim max = 0;

getrlimit(RLIMIT NOFILE, Srls);

if (rls .rlim raax ==0) { .T;;

fprintf( fp, getrlimit failedXn );..

return 1;

for (int i = 1; i < rls.rlim max; i++) (void) closed);

/* все выходные сообщения переадресованы на системную консоль */ int fd = open( /dev/console , 2); (void) dup2(fd, 1); (void) dup2(fd, 2);

setsid 0 ;

time t tim = time(O);

sprintf( msg, svc (%d)starting: %s , getpidO, ctime(Stim) ); write( 1, msg, strlen(msg) );

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

svcp = new RPC svc( 0, transp, SCANPROG, SCANVER );

if (Isvcp II lsvcp->good()) [

sprintf(msg, RPC svc failedXn );

write(1, msg, strlen(msg) );

exit( 1 ,),;

svcp->add j>roc( SCANDIR, scandir );

/* завершить демон после 60 секунд активности */ signal( SIGALRM. (void(*)(int)) done );

svcp->run(); return 0;

В этой программе сервер создает обработчик RPC svc для дескриптора файла 0. При этом используется механизм транспортировки tcp. Он регистрирует функцию scandir как такую, которая может быть вызвана клиентами, а затем устанавливает сигнал SIGALRM для передачи самому себе. Последняя операция обусловлена тем, что inetd не порождает новый процесс для запроса сервиса, если уже есть работающий серверный процесс (это делается для того, чтобы не создавать лишние процессы). Таким образом, сервер, порожденный демоном inetd, после обслуживания запроса чаще всего



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

После регистрации функции done как обработчика сигнала SIGALRM программа-сервер вызывает RPCsvcr.run. Поскольку уже имеется ожидающий запрос, немедленно вызывается функция scandir. После того как функция scandir возвращает управление, сервер блокируется в функции RPC svc.:run, ожидая следующего RPC-вызова. Сервер живет максимум 60 секунд, в течение которых должен поступить новый RPC-вызов. Если это происходит, функция done изменяет установку будильника так, чтобы процесс мог работать еще 60 секунд. При каждом вызове функции scandir устанавливается глобальная переменная workjn progress, которая сбрасывается после вьшолнения функции scandir. В зависимости от значения этой переменной функция done либо завершает процесс, либо перезапускает будильник.

Клиентская и серверная программы компилируются в системе UNIX System V.4 следующим образом (в ONC-системах они компилируются с опцией -DSYSV4):

% СС -с scan2 xdr.c RPC.C

% СС -DSYSV4 -о scan svc3 scan svc3.C scan2 xdr.o \

RPC.о -Isocket -Insl % CC -DSYSV4 -о scan cls2 scan cls2.C scan2 xdr.o \

RPC.о -Isocket -Insl

Элемент файла /etc/inetd.conf ддя сервера scan svc3 в формате System V.4 выглядит следующим образом:

536871168/1 tli rpc/* wait root /proj/scan svc3 scan svc4

Ниже показан пример запуска клиентской и серверной программ. Серверная часть находится на машине fruit, а клиентскую можно запускать на любой машине, соединенной с fruit. Вручную запускается только программа-клиент, а серверная программа вьшолняется демоном inetd:

% scan cls2 fruit

..scan cls2.С ..scan svc2.С ..RPC.C ..RPC.h . .scan2 xdr,c ..scan2.h ..scan svc2 ..scan cls2 Prog 536871168

(version 1) is alive

12.12. Заключение

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

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

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



ГЛАВА


Многопотоковое программирование

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

В многопотоковой программе в каждый момент времени могут выполняться параллельно несколько элементов кода, при этом каждый элемент кода выполняется одним потоком управления. Работая с многопотоковым процессом, пользователь может вводить команды непрерывно, одну за другой, и процесс выполняет все команды параллельно.

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

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

Глава 13. Многапотоиовоапрогрвмиирвммйг*



1 ... 81 82 83 [ 84 ] 85 86 87 ... 98

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