|
Программирование >> Структура ядра и системные вызовы
int msgid; 4.5?/)/ дескриптор очереди сообщений struct mgbuf mObj; L public: (j;. /* конструктор. Получаем указатель на очередь сообщений.*/ message ( int key ) j I t if (msgId=msgget(key,IPC CREAT0666)==-l) < perror( msgget ); /* функция-деструктор. В данном случае ничего не делает */ -message О О; /* проверить, открыта или нет очередь сообщений */ int good О ( return (msgid >= 0) ? 1 : 0; }; /* удалить очередь сообщений *1 int rmQ О int rc=msgctl (msgid, IPC RMID, 0) ; if (rc==-l) perror( msgcti ) ; return rc; /* передать сообщение */ int send ( const void* buf, int size, int type) { , mbbj.mtype = type; memcpy(mObj.mtext,buf,size) ; if (msgsnd(msgid,SmObj,size, 0)) { perror( msgsnd ); return -1; return 0; /* принять сообщение */ int rev ( void* buf, int size, int type, int* rtype) ( int len = msgrcv(msgld,&mObj,MAX LEN,type,MSG NOERROR); if (len==-l) ( perror( msgrcv ); return -1; memcpy(buf,mobj.mtext, len); if (rtype) *rtype = mObj.mtype; return len; #endif Преимущество применения класса message состоит в том, что прикладным программам не нужно использовать базовые API сообщений. Манипулируя типом сообщения и буфером сообщений, они взаимодействуют с объектом этого класса, применяя практически тот же самый интерфейс, который API read и write используют для файлов. Таким образом можно сократить затраты времени на программирование. Более того, в следующем разделе будет показано, как можно изменить класс message для реализации других методов IPC, не корректируя при этом код приложения. На примере приведенной ниже программы server. С показано использование заголовка message.к tinclude <strstream.h> tinclude message.h tinclude <string.h> tinclude <signal.h> int mainO ( int len, pid, cmdid; char buf[256]; time t tim; /* настроить этот процесс как демон */ for (int i=0; i < 20; i++) signal(i, SIG IGN); setsid 0 ; cout server: start executing. . .An flush; message mqueue(MSGKEY); открыть очередь сообщений if (!mqueue.good О) exit(l); выйти из программы, если Очередь не открывается /* ожидать запрос от клиента */ while (len=mqueue.rcv(buf,sizeof(buf),-99,&cmdId) > 0) ( /* получить идентификатор процесса клиента и проверить его достоверность */ istrstream (buf, sizeof (buf) ) pid; if (pid < 100) ( >4W- cerr Illegal PID: buf ; ; pid endl; continue; , , /* подготовить ответ клиенту */ cerr server: receive cmd t cmdld ,from client: pid endl; switch (cmdId) ( case LOCAL TIME: tim = time(O); strcpy (buf, ctime (Stiin)); break; case UTC TIME: tim = time(0); .strcpy(buf,asctime(gmtime(stim))); break; case QUIT CMD: cerr server: deleting msg queue...\n ; return mqueue. rmQO ; default: /* послать клиенту сообщение об ошибке */ ostrstream(buf, sizeof (buf)) Illegal cmd: cmdid \0; Ниже приведен текст программы client. С. /* послать клиенту ответ */ if (mqueue.send(buf,strlen(buf)+1,pid)==-1) cerr Server: getpid() send response fails\n ; }; /* бесконечный цикл */ return 0; Начинается этот серверный процесс с того, что настраивает себя как демон: игнорирует все основные сигналы и делает себя лидером сеанса и группы процессов. Теперь он не зависит от своего родительского процесса. Затем серверный процесс создает объект-сообщение с ключевым идентификатором 176 (он выбран произвольно). Функция-конструктор класса создает очередь сообщений, если она еще не существует, присваивает ей ключеюй идентификатор и предоставляет права на чтение и запись всем пользователям. После создания объекта-сообщения сервер входит в цикл опроса и ожидает передачи клиентскими процессами в этот объект сообщений-запросов на обслуживание. В частности, поскольку типы клиентских команд запроса на обслуживание ограничены диапазоном 1-99, сервер опрашивает сообщения, тип которых не превышает 100. Прочрггав запрос на обслуживание, сервер проверяет, превышает ли идентификатор процесса клиентского запроса значение 99, а затем передает клиенту ответное сообщение на основании полученной команды запроса на обслуживание. Если команда обслуживания - QUIT CMD, сервер удаляет объект-сообщение и завершается. При разработке программы, в которой используются сообщения, следует помнить о том, что у процесса, как правило, редко возникает необходимость чтения посланных им же сообщений. Важно поэтому, чтобы процесс использовал для отправляемых и получаемых сообщений разные наборы типов сообщений. В рассматриваемом примере типами ответных сообщений сервера являются идентификаторы процессов клиентов. Считается, что эти идентификаторы всегда больше или равны 100 (данное огранотение вводится в программе client.С). Кроме того, типами клиентских сообщений-запросов на обслуживание являются типы команд запроса на обслуживание (значения этих типов находятся в диапазоне от 1 до 99). Таким образом, сервер читает только клиентские сообщения-запросы на обслуживание, но не ответные сообщения на них, а клиенты читают только ответные сообщения сервера, но не свои собственные сообщения-запросы на обслуживание. ♦ include <strstream.)i> ♦ include <string.ti> ♦include message.h int main() ( int cmdId, pid; while (getpidO < 100) switch (pid=for)c()) { PID клиентского процесса > 99 case -1: perror ( forjc ) , exit(l); case 0: break; default; waitpid(pid, 0, 0); exit(O); cout client: start executing...\n ; message mqueue(MSGKEY); создать объект-сообщение if (!mqueue.good()) exit(l); выйти из программы, если очередь не открывается chair procrd[256I, buf2[256); ostrstream(procld,sizeof(procid)) getpidO \0; do { /* прочитать команду со стандартного ввода */ cout cmd> flush; вывести на экран приглашение на ввод cin >> cmdId; прочитать команду, поступившую от пользователя cout endl; ввести с консоли <CR> if (cin.eofO) brealc; выйти из программы, если встретился символ EOF /* проверить допустимость команды */ if (Icin.goodO II cmdId < О cmdId > 99) { cerr Invalid input: cmdId endl; continue; /* передать запрос в демон */ if (mqueue.send(procld,strlen(procld),cmdId)) cout client: getpidO msgSnd errorXn ; else if (cmdId==QUIT CMD) breaJc; /* при QUIT CMD выйти */ /* принять данные от демона */ else if (mqueue.rcv(buf2, sizeof(buf2),getpidO,0)==-1) cout client: getpidO msgrcv errorXn ; i /* вывести на экран ответ сервера */ else cout client: getpidO buf2 endl; > while (1); /* цикл до EOF */ гейгГо; P < -iting...\n flush; КОТОРЫЙ SrT P проверяет идентификатор процесса, оторыи всегда больше или равен 100. Если это не так, она рекурсивно вызывает функцию fork до тех пор, пока один из идентификаторов порожденных процессов не будет больше или равен 100. При этом все не удовлетворяющие этому условию родительские процессы просто ждут, пока их порожденный процесс завершится, а затем завершаются сами. Этот простой способ гарантирует соблюдение условия взаимодействия по схеме клиент/сервер (идентификатор клиентского процесса должен быть больше 99). После создания клиентского процесса он открывает объект-сообщение, имеющий тот же ключевой идентификатор, что и сервер. Затем программа начинает цикл, в котором приглашает пользователей пошагово ввести со стандартного устройства ввода команду запроса на обслуживание. Для каждой принимаемой команды программа проверяет принадлежность значения ее типа диапазону 1-99, а затем посылает в серверный процесс сообщение-запрос на обслуживание. После этого программа читает ответ сервера и направляет поступившие данные на стандартное устройство вывода. Клиентский процесс завершается, когда в данных, поступивших на стандартное устройство ввода, встречается признак конца файла (например, пользователь нажимает клавиши [Ctrl+D]) или когда при чтении данных со стандартного устройства ввода возникает ошибка. Вот пример взаимодействия этих программ: chpl3 % server; [1] 356 chpl3 % client: cmci> 1 . server: client: cmd> 2 server: client: cmd> 4 server; client: cmd> 3 client: server: server: [1] Done chpl3% server & start execution. client start execution... receive cmd il, from client: 357 357 Tue Jan 24 22:23:17 1995 receive cmd §2, from client: 357 357 Pfed Jan 25 06:23:19 1995 receive cmd i4, from client: 357 357 Illegal cmd: 4 357 exiting... receive cmd i3, from client: 357 deleting msg queue... server В приведенных выше примерах с сервером взаимодействует только ОДО клиент! но могут взаимодействовать и несколько клиентских процессов одновременно. < 10.4. Сообщения в стандарте POSIX.lb Сообщения POSIX.lb создаются и обрабатываются приблизительно так же, как сообщения UNIX System V. В частности, в стандарте POSIX.lb определяются заголовок <mqueue.h> и набор API сообщений: #include <mqueue.h> mqd t mqjopen ( char* name, int flags, mode t mode, struct mq attr* attrp ); int mq send ( mqd t mqid, const char* msg, size t len, unsigned priority ); int mq receive { mqd t mqid, char* buf, size t len, unsigned* prio ); int mq clo%e ( mqd t mqid ); int mq notify ( mqd t mqid, const struct mq sigevent* sigvp); int mqjgetattr ( mqd t mqid, struct mq attr* atttrp ); int mqjsetattr ( mqd t mqid, struct mq attr* atttrp, struct mq attr* oattrp ); API mqjopen похож на функцию msgget он возвращает идентификатор очереди сообщений типа mqdjt. Отметим, что идентификатор mqdjt не является целочисленным дескриптором. Очереди сообщений, которую открывает функция mqjopen, присваивается имя, указанное в аргументе пате. Значение пате должно быть строкой символов, похожей на путевое UNIX-имя, и всегда начинаться с символа / . Допустимость использования дополнительных символов / зависит от варианта системы. Кроме того, пользователям не следует ожидать, что в результате этого вызова будет создан файл с таким же именем. Аргумент flags задает способ доступа к очереди со стороны вызывающего процесса. Он может иметь следующие значения: 0 RDONLY (вызывающий процесс может только принимать сообщения); 0 WRONLY (вызывающий процесс имеет право лишь передавать сообщения); 0 RDWR (вызывающий процесс может передавать и принимать сообщения). Если указан флаг 0 CREAT, то в случае отсутствия заданной очереди она будет создана. Выполнение функции будет прервано, если за флагом 0 CREAT следует флаг 0 EXCL, но указанная очередь уже существует. Наконец, можно присвоить переменной oflag значение 0 NONBLOCK, которое указывает, что доступ к очереди сообщений (осуществляемый посредством API mq send и mq receive) будет неблокирующим. Аргументы mode и attrp нужны только в том случае, если в аргументе oflag указан флаг 0 CREAT. Они задают для очереди сообщений атрибуты и разрешения на чтение и запись. Тип данных struct mqjattr определяется в заголовке <mqueue.h>. В случае успешного вьшолнения эта функция возвращает идентификатор очереди сообщений типа mqdj, а в случае неудачи - значение {mqd t)-\.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |