Н.В. Вдовикина, И.В. Машечкин, А.Н. Терехин, В.В. Тюляева - Программирование в ОС UNIX на языке Си (1114655), страница 14
Текст из файла (страница 14)
Приложение может определить собственную структуру для сообщения, содержащую в качестве тела любыеполя необходимой длины – важно лишь, чтобы первым полем всегдавыступало целое типа long, соответствующее типу сообщения. Остальные данные в этой структуре системой никак не интерпретируются и передаются в качестве тела сообщения «как есть».В заголовочном файле <sys/msg.h> определена константаMSGMAX, описывающая максимальный размер тела сообщения.
Припопытке отправить сообщение, у которого число элементов в массиве msgtext превышает это значение, системный вызов вернет –1.Четвертый аргумент данного вызова может принимать значения 0 или IPC_NOWAIT. В случае отсутствия флага IPC_NOWAIT вызывающий процесс будет блокирован (т.е. приостановит работу), еслидля посылки сообщения недостаточно системных ресурсов, т.е.
еслиполная длина сообщений в очереди будет больше максимально допустимого. Если же флаг IPC_NOWAIT будет установлен, то в такойситуации выход из вызова произойдет немедленно, и возвращаемоезначение будет равно –1.В случае удачной записи возвращаемое значение вызова равно0.84Получение сообщения.Для получения сообщения имеется функция int msgrcv (intmsqid, void *msgp, size_t msgsz, long msgtyp, intmsgflg). Первые три аргумента – это дескриптор очереди, указа-тель на буфер, куда следует поместить данные, и максимальныйразмер (в байтах) тела сообщения, которое можно туда поместить.Буфер, используемый для приема сообщения, должен иметь структуру, описанную выше.Четвертый аргумент указывает тип сообщения, которое процесс желает получить. Если значение этого аргумента есть 0, то будет получено сообщение любого типа. Если значение аргументаmsgtyp больше 0, из очереди будет извлечено сообщение указанноготипа.
Если же значение аргумента msgtyp отрицательно, то тип принимаемого сообщения определяется как наименьшее значение средитипов, которые меньше модуля msgtyp. В любом случае, как уже говорилось, из подочереди с заданным типом (или из общей очереди,если тип не задан) будет выбрано самое старое сообщение.Последним аргументом является комбинация (побитовое сложение) флагов. Если среди флагов не указан IPC_NOWAIT, и в очереди не найдено ни одного сообщения, удовлетворяющего критериямвыбора, процесс будет заблокирован до появления такого сообщения. Однако, если такое сообщение существует, но его длина превышает указанную в аргументе msgsz, то процесс заблокирован небудет, и вызов сразу вернет –1. Сообщение при этом останется вочереди.
Если же флаг IPC_NOWAIT указан, то вызов сразу вернет –1.Процесс может также указать флаг MSG_NOERROR – в этом случае он может прочитать сообщение, даже если его длина превышаетуказанную емкость буфера. В этом случае в буфер будет записанопервые msgsz байт из тела сообщения, а остальные данные отбрасываются.В случае удачного чтения возвращаемое значение вызова равно фактической длине тела полученного сообщения в байтах.Управление очередью сообщенийДля управления очередью сообщений используется функцияint msgctl(int msqid, int cmd, struct msgid_ds *buf).Данный вызов служит для получения или изменения процессом управляющих параметров, связанных с очередью и уничтоженияочереди.
Его аргументы — идентификатор ресурса, команда, которую необходимо выполнить, и структура, описывающая управляющие параметры очереди. Тип msgid_ds описан в заголовочном файле <sys/message.h>, и представляет собой структуру, в полях кото-85рой хранятся права доступа к очереди, статистика обращений к очереди, ее размер и т.п.Возможные значения аргумента cmd:IPC_STAT – скопировать структуру, описывающую управляющие параметры очереди по адресу, указанному в параметре buf;IPC_SET – заменить структуру, описывающую управляющиепараметры очереди, на структуру, находящуюся по адресу, указанному в параметре buf;IPC_RMID – удалить очередь.
Как уже говорилось, удалитьочередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди,либо процесс с правами привилегированного пользователя.Задача 1. Написать программу: основной процесс читает некоторую текстовую строку из стандартного ввода, и в случае, еслистрока начинается с буквы 'a', эта строка в качестве сообщения будет передана первому потомку, если 'b' - второму потомку, а по получении строки 'q' будет осуществлен выход.
Процессы-потомкираспечатывают полученные строки на стандартный вывод.Основной#include#include#include#include#include#includeпроцесс.<sys/types.h><sys/ipc.h><sys/msg.h><string.h><unistd.h><stdio.h>struct {long mtype;/* тип сообщения */char 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);86/* и копируем ее в буфер сообщения */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':case '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#include#include#include<sys/types.h><sys/ipc.h><sys/msg.h><stdio.h>struct {long mtype;char Data[256];} Message;int main(int argc, char **argv){key_t key; int msgid;87key = ftok("/usr/mash",'s');/* получаем ключ по тем же параметрам */msgid = msgget(key, 0666 | IPC_CREAT);/*подключаемся к очереди сообщений */for(;;) {msgrcv(msgid, (struct msgbuf*) (&Message),256, 1, 0);if (Message.Data[0]=='q' ||Message.Data[0]=='Q') break;printf("\nПроцесс-приемник А: %s",Message.Data);}return 0;}Разделяемая памятьМеханизм разделяемой памяти позволяет нескольким процессам получить отображение некоторых страниц из своей виртуальнойпамяти на общую область физической памяти.
Благодаря этому,данные, находящиеся в этой области памяти, будут доступны длячтения и модификации всем процессам, подключившимся к даннойобласти памяти.Процесс, подключившийся к разделяемой памяти, может затемполучить указатель на некоторый адрес в своем виртуальном адресном пространстве, соответствующий данной области разделяемойпамяти. После этого он может работать с этой областью памяти аналогично тому, как если бы она была выделена динамически (например, путем обращения к malloc()), однако, как уже говорилось, сама по себе разделяемая область памяти не уничтожается автоматически даже после того, как процесс, создавший или использовавшийее, перестанет с ней работатьСоздание общей памятиДля создания объекта разделяемой памяти служит вызов intshmget (key_t key, int size, int shmemflg).Аргументы этого вызова: key - ключ для доступа к разделяемой памяти; size задает размер области памяти, к которой процессжелает получить доступ.
Если в результате вызова shmget() будетсоздана новая область разделяемой памяти, то ее размер будет соответствовать значению size. Если же процесс подключается к существующей области разделяемой памяти, то значение size должнобыть не более ее размера, иначе вызов вернет –1. Если процесс приподключении к существующей области разделяемой памяти указал варгументе size значение, меньшее ее фактического размера, то впо88следствии он сможет получить доступ только к первым size байтамэтой области.В заголовочном файле <sys/shm.h> определены константыSHMMIN и SHMMAX, задающий минимально возможный и максимальновозможный размер области разделяемой памяти.
Если процесс пытается создать область разделяемой памяти, размер которой не удовлетворяет этим границам, системный вызов shmget() окончится неудачей.Третий параметр определяет флаги, управляющие поведениемвызова. Подробнее алгоритм создания/подключения разделяемогоресурса был описан выше.В случае успешного завершения вызов возвращает положительное число – дескриптор области памяти, в случае неудачи - -1.Доступ к разделяемой памятиПри помощи вызова char *shmat(int shmid, char*shmaddr, int shmflg) процесс подсоединяет область разделяемой памяти, дескриптор которой указан в shmid, к своему виртуальному адресному пространству.
После выполнения этой операциипроцесс сможет читать и модифицировать данные, находящиеся вобласти разделяемой памяти, адресуя ее как любую другую областьв своем собственном виртуальном адресном пространстве.В качестве второго аргумента процесс может указать виртуальный адрес в своем адресном пространстве, начиная с которогонеобходимо подсоединить разделяемую память.
Чаще всего, однако,в качестве значения этого аргумента передается 0, что означает, чтосистема сама может выбрать адрес начала разделяемой памяти.Примечание. Передача конкретного адреса в этом параметреимеет смысл в том случае, если, к примеру, в разделяемую памятьзаписываются указатели на нее же (например, в ней хранится связанный список) – в этой ситуации для того, чтобы использованиеэтих указателей имело смысл и было корректным для всех процессов, подключенных к памяти, важно, чтобы во всех процессах адресначала области разделяемой памяти совпадал.Третий аргумент представляет собой комбинацию флагов.