Самодел 2 (1114717), страница 14
Текст из файла (страница 14)
Отметим, что даже если ни один процесс не подключен к разделяемому ресурсу, система не удаляет его автоматически. Удаление объектов IPC является обязанностью одного из работающих с ним процессов и для этого определена специальная функция. Для этого системой предоставляются соответствующие функции по управлению объектами System V IPC.
IPC: очередь сообщений.
Итак, одним из типов объектов System V IPC являются очереди сообщений. Очередь сообщений представляет собой некое хранилище типизированных сообщений, организованное по принципу FIFO. Любой процесс может помещать новые сообщения в очередь и извлекать из очереди имеющиеся там сообщения. Каждое сообщение имеет тип, представляющий собой некоторое целое число. Благодаря наличию типов сообщений, очередь можно интерпретировать двояко — рассматривать ее либо как сквозную очередь неразличимых по типу сообщений, либо как некоторое объединение подочередей, каждая из которых содержит элементы определенного типа. Извлечение сообщений из очереди происходит согласно принципу FIFO – в порядке их записи, однако процесс-получатель может указать, из какой подочереди он хочет извлечь сообщение, или, иначе говоря, сообщение какого типа он желает получить – в этом случае из очереди будет извлечено самое «старое» сообщение нужного типа.
Например,
В
А
А
В
А
В
А
Рассмотрим набор системных вызовов, поддерживающий работу с очередями сообщений.
Доступ к очереди сообщений.
Для создания новой или для доступа к существующей используется системный вызов:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
int msgget (key_t key, int msgflag)
key – ключ
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)
msqid – идентификатор очереди, полученный в результате вызова msgget()
msgp – указатель на буфер следующей структуры:
long msgtype -тип сообщения, подлежащего посылке в очередь
char msgtext[ ] –реальные данные (тело сообщения)
msgsz –размер буфера (не более определенной в заголовочном файле <sys/msg.h> константы MSGMAX) (При попытке отправить сообщение, у которого число элементов в массиве msgtext превышает это значение, системный вызов вернет –1.)
msgflg = 0 вызывающий процесс блокируется, если для посылки сообщения недостаточно системных ресурсов, т.е. если полная длина сообщений в очереди будет больше максимально допустимого.
= 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, long msgtyp, int msgflg)
msqid – идентификатор очереди
msgp – указатель на буфер
msgsz – размер буфера
(Первые три аргумента аналогичны аргументам предыдущего вызова)
msgtyp тип сообщения, которое процесс желает получить
= 0 любого типа
> 0 типа msgtyp
< 0 наименьшее значение среди типов, которые меньше модуля msgtyp
(В любом случае, как уже говорилось, из подочереди с заданным типом (или из общей очереди, если тип не задан) будет выбрано самое старое сообщение.
)
msgflg – побитовое сложение флагов
IPC_NOWAIT – если сообщения в очереди нет, то возврат –1
MSG_NOERROR – разрешение получать сообщение, даже если его длина превышает емкость буфера. В этом случае в буфер будет записано первые msgsz байт из тела сообщения, а остальные данные отбрасываются.
если же среди флагов не указан IPC_NOWAIT, и в очереди не найдено ни одного сообщения, удовлетворяющего критериям выбора, процесс будет заблокирован до появления такого сообщения. (Однако, если такое сообщение существует, но его длина превышает указанную в аргументе msgsz, то процесс заблокирован не будет, и вызов сразу вернет –1. Сообщение при этом останется в очереди)
В случае удачного чтения возвращаемое значение вызова равно 0.
Управление очередью сообщений.
Функция управления очередью сообщений выглядит следующим образом:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl (int msqid, int cmd, struct msgid_ds *buf)
msgid – идентификатор ресурса
cmd – команда
IPC_STAT – скопировать структуру, описывающую управляющие параметры очереди по адресу, указанному в параметре buf
IPC_SET – заменить структуру, описывающую управляющие параметры очереди, на структуру, находящуюся по адресу, указанному в параметре buf
IPC_RMID – удалить очередь. Как уже говорилось, удалить очередь может только процесс, у которого эффективный идентификатор пользователя совпадает с владельцем или создателем очереди, либо процесс с правами привилегированного пользователя.
buf – структура, описывающая параметры очереди.
Тип msgid_ds описан в заголовочном файл <sys/message.h>, и представляет собой структуру, в полях которой хранятся права доступа к очереди, статистика обращений к очереди, ее размер и т.п.
Данный вызов используется для получения или изменения процессом управляющих параметров, связанных с очередью и уничтожения очереди.
Пример. Использование очереди сообщений.
Пример программы, где основной процесс читает некоторую текстовую строку из стандартного ввода и в случае, если строка начинается с буквы 'a', то эта строка в качестве сообщения будет передана процессу А, если 'b' - процессу В, если 'q' - то процессам А и В, затем будет осуществлен выход. Процессы А и В распечатывают полученные строки на стандартный вывод.
Основной процесс.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
#include <stdio.h>
struct {long mtype; /* тип сообщения */
char Data[256]; /* сообщение */
} Message;
int main(int argc, chr **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);
/* уничтожаем очередь*/
exit(0);
default: break;
}
}
}
Процесс-приемник А
/* процесс В аналогичен с точностью до четвертого параметра
в msgrcv */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/message.h>
#include <stdio.h>
struct { long mtype;
char Data[256];
} Message;
int main(int argc, chr **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*/
printf("%s",Message.Data);
if (Message.Data[0]='q' || Message.Data[0]='Q') break;
}
exit();
}
Пример. Очередь сообщений. Модель «клиент-сервер» .
Благодаря наличию типизации сообщений, очередь сообщений предоставляет возможность мультиплексировать сообщения от различных процессов, при этом каждая пара взаимодействующих через очередь процессов может использовать свой тип сообщений, и таким образом, их данные не будут смешиваться.
В качестве иллюстрации приведем следующий стандартный пример взаимодействия. Рассмотрим еще один пример - пусть существует процесс-сервер и несколько процессов-клиентов. Все они могут обмениваться данными, используя одну очередь сообщений. Для этого сообщениям, направляемым от клиента к серверу, присваиваем значение типа 1. При этом процесс, отправивший сообщение, в его теле передает некоторую информацию, позволяющую его однозначно идентифицировать. Тогда сервер, отправляя сообщение конкретному процессу, в качестве его типа указывает эту информацию (например, PID процесса). Таким образом, сервер будет читать из очереди только сообщения типа 1, а клиенты — сообщения с типами, равными идентификаторам их процессов.
Server
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, chr **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), 1, 0)<=0) continue;
messageto.mestype=messagefrom.mes;
strcpy( messageto.mes, "Message for client");
msgsnd (mesid, &messageto, sizeof(messageto), 0);
}
msgctl (mesid, IPC_RMID, 0);
return 0;
}
client
#include <sys/types.h>