Билеты (Graur) (1114774), страница 21
Текст из файла (страница 21)
В качестве значения ключа при создании любого IPCобъекта может быть указано значение IPC_PRIVATE. При этом создается ресурс,который будет доступен только породившему его процессу. Такие ресурсы обычнопорождаются родительским процессом, который затем сохраняет полученныйдескриптор в некоторой переменной и порождает своих потомков. Так какпотомкам доступен уже готовый дескриптор созданного объекта, они могутнепосредственно работать с ним, не обращаясь предварительно к «get»-методу.Таким образом, созданный ресурс может совместно использоваться родительскими порожденными процессами.
Однако, важно понимать, что если один из этихпроцессов повторно вызовет «get»-метод с ключом IPC_PRIVATE, в результатебудет получен другой, совершенно новый разделяемый ресурс, так как приобращении к «get»-методу с ключом IPC_PRIVATE всякий раз создается новыйобъект нужного типа.Отметим, что даже если ни один процесс не подключен к разделяемомуресурсу, система не удаляет его автоматически. Удаление объектов IPC являетсяобязанностью одного из работающих с ним процессов и для этого определенаспециальная функция. Для этого системой предоставляются соответствующиефункции по управлению объектами System V IPC.БИЛЕТ 33Очередь сообщений.Итак, одним из типов объектов System V IPC являются очереди сообщений.Очередь сообщений представляет собой некое хранилище типизированныхсообщений, организованное по принципу FIFO.
Любой процесс может помещатьновые сообщения в очередь и извлекать из очереди имеющиеся там сообщения.Каждое сообщение имеет тип, представляющий собой некоторое целое число.Благодаря наличию типов сообщений, очередь можно интерпретировать двояко —рассматривать ее либо как сквозную очередь неразличимых по типу сообщений,либо как некоторое объединение подочередей, каждая из которых содержитэлементы определенного типа. Извлечение сообщений из очереди происходитсогласно принципу FIFO – в порядке их записи, однако процесс-получатель можетуказать, из какой подочереди он хочет извлечь сообщение, или, иначе говоря,сообщение какого типа он желает получить – в этом случае из очереди будетизвлечено самое «старое» сообщение нужного типа (см.
Рис. 10).ВААВАВАРис. 10 Типизированные очереди сообщенийРассмотрим набор системных вызовов, поддерживающий работу с очередямисообщений.Доступ к очереди сообщений.Для создания новой или для доступа к существующей используется системныйвызов:#INCLUDE <SYS/TYPES.H>#INCLUDE <SYS/IPC.H>#INCLUDE <SYS/MESSAGE.H>INT MSGGET (KEY_T KEY, INT MSGFLAG)В случае успеха вызов возвращает положительный дескриптор очереди, которыйможет в дальнейшем использоваться для операций с ней, в случае неудачи -1.Первым аргументом вызова является ключ, вторым – флаги, управляющиеповедением вызова. Подробнее детали процесса создания/подключения к ресурсуописаны выше.Отправка сообщения.Для отправки сообщения используется функция msgsnd():#INCLUDE <SYS/TYPES.H>#INCLUDE <SYS/IPC.H>#INCLUDE <SYS/MSG.H>INT MSGSND (INT MSQID, CONST VOID *MSGP, SIZE_T MSGSZ,INT MSGFLG)Ее первый аргумент — идентификатор очереди, полученный в результате вызоваmsgget().
Второй аргумент — указатель на буфер, содержащий реальные данныеи тип сообщения, подлежащего посылке в очередь, в третьем аргументеуказывается размер буфера.В качестве буфера необходимо указывать структуру, содержащую следующие поля(в указанном порядке):long msgtype — тип сообщенияchar msgtext[ ]— данные (тело сообщения)В заголовочном файле <sys/msg.h> определена константа MSGMAX,описывающая максимальный размер тела сообщения.
При попытке отправитьсообщение, у которого число элементов в массиве msgtext превышает этозначение, системный вызов вернет –1.Четвертый аргумент данного вызова может принимать значения 0 илиIPC_NOWAIT. В случае отсутствия флага IPC_NOWAIT вызывающий процессбудет блокирован (т.е. приостановит работу), если для посылки сообщениянедостаточно системных ресурсов, т.е. если полная длина сообщений в очередибудет больше максимально допустимого. Если же флаг IPC_NOWAIT будетустановлен, то в такой ситуации выход из вызова произойдет немедленно, ивозвращаемое значение будет равно –1.В случае удачной записи возвращаемое значение вызова равно 0.Получение сообщения.Для получения сообщения имеется функция msgrcv():#INCLUDE <SYS/TYPES.H>#INCLUDE <SYS/IPC.H>#INCLUDE <SYS/MSG.H>INT MSGRCV (INT MSQID, VOID *MSGP, SIZE_T MSGSZ, LONGMSGTYP, INT MSGFLG)Первые три аргумента аналогичны аргументам предыдущего вызова: этодескриптор очереди, указатель на буфер, куда следует поместить данные, имаксимальный размер (в байтах) тела сообщения, которое можно туда поместить.Буфер, используемый для приема сообщения, должен иметь структуру, описаннуювыше.Четвертый аргумент указывает тип сообщения, которое процесс желает получить.Если значение этого аргумента есть 0, то будет получено сообщение любого типа.Если значение аргумента msgtyp больше 0, из очереди будет извлеченосообщение указанного типа.
Если же значение аргумента msgtyp отрицательно, тотип принимаемого сообщения определяется как наименьшее значение среди типов,которые меньше модуля msgtyp. В любом случае, как уже говорилось, изподочереди с заданным типом (или из общей очереди, если тип не задан) будетвыбрано самое старое сообщение.Последним аргументом является комбинация (побитовое сложение) флагов. Еслисреди флагов не указан IPC_NOWAIT, и в очереди не найдено ни одногосообщения, удовлетворяющего критериям выбора, процесс будет заблокирован допоявления такого сообщения. (Однако, если такое сообщение существует, но егодлина превышает указанную в аргументе msgsz, то процесс заблокирован небудет, и вызов сразу вернет –1. Сообщение при этом останется в очереди).
Если жефлаг IPC_NOWAIT указан, то вызов сразу вернет –1.Процесс может также указать флаг MSG_NOERROR – в этом случае он можетпрочитать сообщение, даже если его длина превышает указанную емкость буфера.В этом случае в буфер будет записано первые msgsz байт из тела сообщения, аостальные данные отбрасываются.В случае удачного чтения возвращаемое значение вызова равно фактической длинетела полученного сообщения в байтах.Управление очередью сообщений.Функция управления очередью сообщений выглядит следующим образом:#INCLUDE <SYS/TYPES.H>#INCLUDE <SYS/IPC.H>#INCLUDE <SYS/MSG.H>INT MSGCTL(INT MSQID, INT CMD, STRUCT MSGID_DS *BUF)Данный вызов используется для получения или изменения процессомуправляющих параметров, связанных с очередью и уничтожения очереди.
Ееаргументы — идентификатор ресурса, команда, которую необходимо выполнить, иструктура, описывающая управляющие параметры очереди. Тип msgid_ds описанв заголовочном файле <sys/message.h>, и представляет собой структуру, вполях которой хранятся права доступа к очереди, статистика обращений к очереди,ее размер и т.п.Возможные значения аргумента cmd:IPC_STAT – скопировать структуру, описывающую управляющие параметрыочереди по адресу, указанному в параметре bufIPC_SET – заменить структуру, описывающую управляющие параметры очереди,на структуру, находящуюся по адресу, указанному в параметре bufIPC_RMID – удалить очередь.
Как уже говорилось, удалить очередь может толькопроцесс, у которого эффективный идентификатор пользователя совпадает свладельцем или создателем очереди, либо процесс с правами привилегированногопользователя.10. Использование очереди сообщений.Пример программы, где основной процесс читает некоторую текстовую строку изстандартного ввода, и в случае, если строка начинается с буквы '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;/* тип сообщения */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);/* и копируем ее в буфер сообщения */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;key = ftok("/usr/mash",'s');/* получаем ключ по тем же параметрам */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Процесс-приемник А: %s",Message.Data);}return 0;}Благодаря наличию типизации сообщений, очередь сообщенийпредоставляет возможность мультиплексировать сообщения от различныхпроцессов, при этом каждая пара взаимодействующих через очередь процессовможет использовать свой тип сообщений, и таким образом, их данные не будутсмешиваться.В качестве иллюстрации приведем следующий стандартный примервзаимодействия.
Рассмотрим еще один пример - пусть существует процесс-сервери несколько процессов-клиентов. Все они могут обмениваться данными,используя одну очередь сообщений. Для этого сообщениям, направляемым отклиента к серверу, присваиваем значение типа 1. При этом процесс, отправившийсообщение, в его теле передает некоторую информацию, позволяющую егооднозначно идентифицировать. Тогда сервер, отправляя сообщение конкретномупроцессу, в качестве его типа указывает эту информацию (например, PIDпроцесса).