Н.В. Вдовикина, И.В. Машечкин, А.Н. Терехин, А.Н. Томилин - Операционные системы - взаимодействие процессов (2008) (1114653), страница 19
Текст из файла (страница 19)
Приложение может определитьсобственную структуру для сообщения, содержащую в качестве телалюбые поля необходимой длины – важно лишь, чтобы первымполем всегда выступало целое типа long, соответствующее типусообщения. Остальные данные в этой структуре системой никак неинтерпретируются и передаются в качетсве тела сообщения «какесть».В заголовочном файле <sys/msg.h> определена константаMSGMAX, описывающая максимальный размер тела сообщения. Припопытке отправить сообщение, у которого число элементов вмассиве msgtext превышает это значение, системный вызов вернет–1.Четвертый аргумент данного вызова может приниматьзначения 0 или IPC_NOWAIT.
В случае отсутствия флага IPC_NOWAITвызывающий процесс будет блокирован (т.е. приостановит работу),если для посылки сообщения недостаточно системных ресурсов, т.е.если полная длина сообщений в очереди будет больше максимальнодопустимого. Если же флаг IPC_NOWAIT будет установлен, то в такойситуации выход из вызова произойдет немедленно, и возвращаемоезначение будет равно –1.В случае удачной записи возвращаемое значение вызова равно0.5.2.3 Получение сообщенияДля получения сообщения имеется функция msgrcv():#include <sys/types.h>#include <sys/ipc.h>111#include <sys/msg.h>int msgrcv (int msqid,long msgtyp, int msgflg);void *msgp, size_t msgsz,Первые три аргумента аналогичны аргументам предыдущеговызова: это дескриптор очереди, указатель на буфер, куда следуетпоместить данные, и максимальный размер (в байтах) теласообщения, которое можно туда поместить.
Буфер, используемыйдля приема сообщения, должен иметь структуру, описанную выше.Четвертый аргумент указывает тип сообщения, котороепроцесс желает получить. Если значение этого аргумента есть 0, тобудет получено сообщение любого типа. Если значение аргументаmsgtyp больше 0, из очереди будет извлечено сообщение указанноготипа. Если же значение аргумента msgtyp отрицательно, то типпринимаемого сообщения определяется как наименьшее значениесреди типов, которые меньше модуля msgtyp.
В любом случае, какуже говорилось, из подочереди с заданным типом (или из общейочереди, если тип не задан) будет выбрано самое старое сообщение.Последним аргументом является комбинация (побитовоесложение) флагов. Если среди флагов не указан IPC_NOWAIT, и вочереди не найдено ни одного сообщения, удовлетворяющегокритериям выбора, процесс будет заблокирован до появления такогосообщения. (Однако, если такое сообщение существует, но егодлина превышает указанную в аргументе msgsz, то процессзаблокирован не будет, и вызов сразу вернет –1. Сообщение приэтом останется в очереди).
Если же флаг IPC_NOWAIT указан, товызов сразу вернет –1.Процесс может также указать флаг MSG_NOERROR – в этомслучае он может прочитать сообщение, даже если его длинапревышает указанную емкость буфера. В этом случае в буфер будетзаписано первые msgsz байт из тела сообщения, а остальные данныеотбрасываются.В случае удачного чтения возвращаемое значение вызоваравно фактической длине тела полученного сообщения в байтах.5.2.4 Управление очередью сообщенийФункция управленияследующим образом:очередью#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>112сообщенийвыглядитint*buf);msgctl(intmsqid,intcmd,structmsgid_dsДанный вызов используется для получения или измененияпроцессом управляющих параметров, связанных с очередью иуничтожения очереди.
Ее аргументы — идентификатор ресурса,команда, которую необходимо выполнить, и структура,описывающая управляющие параметры очереди. Тип msgid_dsописан в заголовочном файле <sys/message.h>, и представляетсобой структуру, в полях которой хранятся права доступа к очереди,статистика обращений к очереди, ее размер и т.п.Возможные значения аргумента cmd:–скопироватьструктуру,описывающуюуправляющие параметры очереди по адресу, указанному в параметреbuf;IPC_STATIPC_SET – заменить структуру, описывающую управляющиепараметры очереди, на структуру, находящуюся по адресу,указанному в параметре buf;IPC_RMID – удалить очередь. Как уже говорилось, удалитьочередь может только процесс, у которого эффективныйидентификатор пользователя совпадает с владельцем или создателемочереди, либо процесс с правами привилегированного пользователя.Пример 24.
Использование очереди сообщений.Пример программы, где основной процесс читает некоторуютекстовую строку из стандартного ввода, и в случае, если строканачинается с буквы 'a', эта строка в качестве сообщения будетпередана процессу А, если 'b' - процессу В, если 'q' - то процессам Аи В, затем будет осуществлен выход. Процессы А и Враспечатывают полученные строки на стандартный вывод.Основной процесс.#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <string.h>#include <unistd.h>#include <stdio.h>struct {long mtype;/* тип сообщения */113char Data[256];/* сообщение */} Message;int main(int argc, char **argv){key_t key;int msgid;char str[256];key = ftok("/usr/mash",'s');/*получаемуникальныйключ,определяющий доступ к ресурсу */однозначноmsgid=msgget(key, 0666 | IPC_CREAT);/*создаем очередь сообщений , 0666 определяетправа доступа */for(;;) {/* запускаем вечный цикл */gets(str); /* читаем из стандартного вводастроку */strcpy(Message.Data, str);/* и копируем ее в буфер сообщения */switch(str[0]){case 'a':case 'A':Message.mtype = 1;/* устанавливаем тип */msgsnd(msgid, (struct msgbuf*)(&Message), strlen(str) + 1, 0);/* посылаем сообщение в очередь*/break;case 'b':case 'B':Message.mtype = 2;msgsnd(msgid, (struct msgbuf*)(&Message), strlen(str) + 1, 0);break;case 'q':114case 'Q':Message.mtype = 1;msgsnd(msgid, (struct msgbuf*)(&Message), strlen(str) + 1, 0);Message.mtype = 2;msgsnd(msgid, (struct msgbuf*)(&Message), strlen(str) + 1, 0);sleep(10);/*ждемполученияпроцессами А и В */сообщенийmsgctl(msgid, IPC_RMID, NULL);/* уничтожаем очередь*/return 0;default:break;}}}Процесс-приемник А/* процесс В аналогичен с точностью до четвертогопараметра в msgrcv */#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <stdio.h>struct {long mtype;char Data[256];} Message;int main(int argc, char **argv){key_t key;int msgid;key = ftok("/usr/mash",'s');115/* получаем ключ по тем же параметрам */msgid = msgget(key, 0666 | IPC_CREAT);/*подключаемся к очереди сообщений */for(;;) {/* запускаем вечный цикл */msgrcv(msgid, (struct msgbuf*) (&Message),256, 1, 0);/* читаем сообщение с типом 1*/if(Message.Data[0]=='q'Message.Data[0]=='Q') break;printf("\nПроцесс-приемникMessage.Data);А:||%s",}return 0;}Благодаря наличию типизации сообщений, очередь сообщенийпредоставляет возможность мультиплексировать сообщения отразличных процессов, при этом каждая пара взаимодействующихчерез очередь процессов может использовать свой тип сообщений, итаким образом, их данные не будут смешиваться.В качестве иллюстрации приведем следующий стандартныйпример взаимодействия.
Рассмотрим еще один пример - пустьсуществует процесс-сервер и несколько процессов-клиентов. Всеони могут обмениваться данными, используя одну очередьсообщений. Для этого сообщениям, направляемым от клиента ксерверу, присваиваем значение типа 1. При этом процесс,отправивший сообщение, в его теле передает некоторуюинформацию, позволяющую его однозначно идентифицировать.Тогда сервер, отправляя сообщение конкретному процессу, вкачестве его типа указывает эту информацию (например, PIDпроцесса). Таким образом, сервер будет читать из очереди толькосообщения типа 1, а клиенты — сообщения с типами, равнымиидентификаторам их процессов.Пример 25.
Очередь сообщений. Модель «клиент-сервер»server#include <sys/types.h>#include <sys/ipc.h>116#include <sys/msg.h>#include <string.h>int main(int argc, char **argv){struct {long mestype;char mes [100];} messageto;struct {long mestype;long mes;} messagefrom;key_t key;int mesid;key = ftok("example",'r');mesid = msgget (key, 0666 | IPC_CREAT);while(1){/* проверка поступления новых сообщений внеблокирующем режиме */if (msgrcv(mesid, &messagefrom,sizeof(messagefrom) – sizeof(long), 1,IPC_NOWAIT) <= 0) continue;/* обработка запроса */messageto.mestype = messagefrom.mes;strcpy( messageto.mes, "Message forclient");/* отправка ответа */msgsnd (mesid, &messageto,sizeof(messageto) – sizeof(long), 0);}msgctl (mesid, IPC_RMID, 0);return 0;117}client#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <unistd.h>#include <stdio.h>int main(int argc, char **argv){struct {long mestype;сообщения*//*описание структурыlong mes;} messageto;struct {long mestype;сообшения*//*описание структурыchar mes[100];} messagefrom;key_t key;int mesid;long pid = getpid();key = ftok("example", 'r');mesid = msgget(key, 0);очереди сообщений*//*присоединение кmessageto.mestype = 1;messageto.mes = pid;msgsnd (mesid, &messageto, sizeof(messageto) –sizeof(long), 0); /* отправка запроса */118/*ожидаем поступления ответного сообщения внеблокирующем режиме (активное ожидание) */while ( msgrcv (mesid, &messagefrom,sizeof(messagefrom) - sizeof(long), pid,IPC_NOWAIT) <= 0);/* сообщение получено */printf("%s\n", messagefrom.mes);return 0;}5.3 Разделяемая памятьФизическая памятьВиртуальное адресноепр-во процессаВиртуальное адресноепр-во процессаРис.