
учебник
.pdf7.4 Удаление очереди сообщений из системы с помощью команды ipcrm или системного вызова msgctl()
После завершения процессов, использовавших очередь сообщений, она не удаляется из системы автоматически, а продолжает сохраняться в системе вместе со всеми невостребованными сообщениями до тех пор, пока не будет выполнена специальная команда или специальный системный вызов. Для удаления очереди сообщений можно воспользоваться командой ipcrm, которая в этом случае примет вид:
ipcrm msg <IPC идентификатор>
Если какой-либо процесс находился в состоянии «ожидание» при выполнении системного вызова msgrcv() или msgsnd() для удаляемой очереди, то он будет разблокирован, и системный вызов констатирует наличие ошибочной ситуации.
Можно удалить очередь сообщений и с помощью системного вызова msgctl().
Системный вызов msgctl()
Прототип системного вызова
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Описание системного вызова
Системный вызов msgctl предназначен для получения информации об очереди сообщений, изменения ее атрибутов и удаления из системы. Данное описание не является полным описанием системного вызова. Для изучения полного описания обращайтесь к
UNIX Manual.
171

Внашем курсе мы будем пользоваться системным вызовом msgctl только для удаления очереди сообщений из системы. Параметр msqid является дескриптором System V IPC для очереди сообщений, т. е. значением, которое вернул системный вызов msgget() при создании очереди или при ее поиске по ключу.
Вкачестве параметра cmd в рамках нашего курса мы всегда будем передавать значение IPC_RMID – команду для удаления очереди сообщений с заданным идентификатором. Параметр buf для этой команды не используется, поэтому мы всегда будем подставлять туда значение NULL.
Возвращаемое значение Системный вызов возвращает значение 0 при нормальном завершении
и значение -1 при возникновении ошибки.
7.5 Пример использования очереди сообщений
Процесс-родитель создает очередь сообщений и порождает три дочерних процесса, процесс-родитель и его потомки вычисляют сумму элементов определенной части массива, процессы-потомки записывают вычисленную сумму в очередь сообщений. Родительский процесс дожидается поступления трех сообщений и выводит на экран окончательный результат – сумму всех элементов массива.
Рис. 7.1. Схема процессов
172
Программа 7-01, пример использования очереди сообщений
#include <stdio.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/msg.h> #include <iostream> using namespace std;
int msgid; //для хранения дескриптора очереди сообщений
int A[100]; //массив, сумма элементов которого вычисляется процессами
struct mymsg //структура для сообщений
{
int mtype; //тип сообщения
int mdata; //данные сообщения
}
m;
int summa (int p) //для вычисления суммы части элементов массива
{
int i, sum = 0 ; //для суммирования элементов
int begin =25*p; //индекс массива, с которого начинается суммирование
int end = begin+25; //индекс массива, на котором завершается суммирование
for(i=begin; i<end; i++) sum+=A[i]; //вычисление суммы части элементов массива
m.mtype = 1; //установить тип сообщения в 1 m.mdata=sum; //записать вычисленную сумму в сообщение
msgsnd(msgid, &m, 2, 0); //послать сообщение в очередь, объем 2 байта
return sum ; //возвратить вычисленную сумму
}
int main()
{//тут должна быть инициализация элементов массива А // создать очередь сообщений
msgid = msgget(IPC_PRIVATE, IPC_CREAT|0666);
if (msgid < 0 ) //если не удалось создать очередь сообщений, завершить выполнение
173
{
cout<<"Ошибка"<<endl; return 0;
}
for (int i=0 ; i<3 ; i++) //создать три процесса-потомка
{
if ( fork() == 0 ) //истинно для дочернего процесса
{
summa(i); return 1;
}
}//родительский процесс вычисляет последнюю четверть массива int rez = summa(3);
for (int i=0 ; i<3 ; i++) //дождаться получения трех сообщений
{
msgrcv(msgid, &m, 2, 0, 0); //тип получаемого сообщения не важен rez += m.mdata; //добавить данные сообщения к результату
}cout<<"Сумма = "<< rez<<endl; //вывести на экран сумму всех элементов массива
return 1;
Листинг 7-01. Пример использования очереди сообщений
Программа 7-02а, пример с однонаправленной передачей текстовой информации.
Эта программа получает доступ к очереди сообщений, отправляет в нее 5 текстовых сообщений с типом 1 и одно пустое сообщение с типом 255, которое будет служить для программы 7-02b сигналом прекращения работы.
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #include <stdio.h> #include <iostream> using namespace std;
#define LAST_MESSAGE 255 /* Тип сообщения для прекращения
174
работы программы 2b */ int main()
{
int msqid; /* IPC дескриптор для очереди сообщений */
char pathname[] = "Имя файла"; /* Имя файла, использующееся для генерации ключа. Файл с таким именем должен существовать в текущей директории */
key_t key; /* IPC ключ */
int i, len; /* Счетчик цикла и длина информативной части сообщения*/ /* Ниже следует пользовательская структура для сообщения */
struct mymsgbuf
{
long mtype; char mtext[81];
}
mybuf;
/* Генерирование IPC ключа из имени файла 2a в текущей директории
иномера экземпляра очереди сообщений 0. */ if((key = ftok(pathname,0)) < 0)
{
cout<<"Can't generate key"<<endl; exit(-1);
}
/* Попытка получить доступ по ключу к очереди сообщений, если она существует, или создать ее, с правами доступа read & write для всех пользователей */
if((msqid = msgget(key, 0666 | IPC_CREAT)) < 0)
{
cout<<"Can't get msqid "<< endl; exit(-1);
}
/* Посылка в цикле 5 сообщений с типом 1 в очередь сообщений, идентифицируемую msqid.*/
for (i = 1; i <= 5; i++)
{
/* Сначала заполнение структуры для сообщения и определение длины информативной части */
175
mybuf.mtype = 1;
strcpy(mybuf.mtext, "This is text message"); len = strlen(mybuf.mtext)+1;
/* Отправка сообщения. В случае ошибки сообщение об этом и удаление очереди сообщений из системы. */
if (msgsnd(msqid, (struct msgbuf *) &mybuf, len, 0) < 0)
{
cout<<"Can't send message to queue"<< endl; msgctl(msqid, IPC_RMID, (struct msqid_ds *) NULL); exit(-1);
}
}
/* Отправка сообщения, которое заставит получающий процесс прекратить работу, с типом LAST_MESSAGE и длиной 0 */
mybuf.mtype = LAST_MESSAGE; len = 0;
if (msgsnd(msqid, (struct msgbuf *) &mybuf, len, 0) < 0)
{
cout<<"Can't send message to queue "<< endl; msgctl(msqid, IPC_RMID, (struct msqid_ds *) NULL); exit(-1);
}
return 0;
}
Листинг 7-02a. Программа 6-02a для иллюстрации работы с очередями сообщений.
Программа 7-02b. Пример использования очереди сообщений.
Эта программа получает доступ к очереди сообщений и читает из нее сообщения с любым типом в порядке FIFO до тех пор, пока не получит сообщение с типом 255, которое будет служить сигналом прекращения работы.
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h>
176
#include <stdio.h> #include <iostream> using namespace std;
#define LAST_MESSAGE 255 /* Тип сообщения для прекращения работы */
int main()
{
int msqid; /* IPC дескриптор для очереди сообщений */
char pathname[] = "Имя файла"; /* Имя файла, использующееся для генерации ключа. Файл с таким именем должен существовать в текущей директории */
key_t key; /* IPC ключ */
int len, maxlen; /* Реальная длина и максимальная длина информативной части сообщения */ /* Ниже следует пользовательская структура для сообщения */
struct mymsgbuf
{
long mtype; char mtext[81];
}
mybuf;
/* Генерирование IPC ключа из имени файла 2a в текущей директории и номера экземпляра очереди сообщений 0 */
if((key = ftok(pathname,0)) < 0)
{
cout<<"Can't generate key"<<endl; exit(-1);
}
/* Попытка получить доступ по ключу к очереди сообщений, если она существует, или создать ее, с правами доступа read & write для всех пользователей */
if((msqid = msgget(key, 0666 | IPC_CREAT)) < 0)
{
cout<<"Can't get msqid"<<endl; exit(-1);
}
while(1)
{
177
/* В бесконечном цикле принятие сообщения любого типа в порядке FIFO с максимальной длиной информативной части 81 символ до тех пор, пока не поступит сообщение с типом LAST_MESSAGE*/
maxlen = 81;
if(( len = msgrcv(msqid, (struct msgbuf *) &mybuf, maxlen, 0, 0) < 0)
{
cout<<"Can't receive message from queue"<<endl; exit(-1);
}
/* Если принятое сообщение имеет тип LAST_MESSAGE, прекращение работы и удаление очереди сообщений из системы. В противном случае вывод принятого сообщения. */
if (mybuf.mtype == LAST_MESSAGE)
{
msgctl(msqid, IPC_RMID, (struct msqid_ds *) NULL); exit(0);
}
cout<<"message type =” << mybuf.mtype<< “info =” << mybuf.mtext<< endl;
}
return 0; /* Исключительно для отсутствия warning'ов при компиляции. */
}
Листинг 7-02b. Программа 7-02b для иллюстрации работы с очередями сообщений
Первая из этих программ посылает пять текстовых сообщений с типом 1 и одно сообщение нулевой длины с типом 255 второй программе. Вторая программа в цикле принимает сообщения любого типа в порядке FIFO и печатает их содержимое до тех пор, пока не получит сообщение с типом 255. Сообщение с типом 255 служит для нее сигналом к завершению работы и ликвидации очереди сообщений. Если перед запуском любой из программ очередь сообщений еще отсутствовала в системе, то программа создаст ее. Необходимо обратить внимание на использование сообщения с типом 255 в качестве сигнала прекращения работы второго процесса. Это сообщение имеет нулевую длину, так как его информативность исчерпывается самим фактом наличия сообщения.
178
Модификация предыдущего примера для передачи числовой информации. В описании системных вызовов msgsnd() и msgrcv() говорится о том, что передаваемая информации не обязательно должна представлять собой текст. Можно воспользоваться очередями сообщений для передачи данных любого вида. При передаче разнородной информации целесообразно информативную часть объединять внутри сообщения в отдельную структуру, для правильного вычисления длины информативной части.
struct mymsgbuf { long mtype; struct {
short sinfo; float finfo;
}info;
}mybuf;
В некоторых вычислительных системах числовые данные размещаются в памяти с выравниванием на определенные адреса (например, на адреса, кратные 4).
Поэтому реальный размер памяти, необходимой для размещения нескольких числовых данных, может оказаться больше суммы длин этих данных, т. е. в нашем случае
sizeof(info)>=sizeof(short)+sizeof(float)
Для полной передачи информативной части сообщения в качестве длины нужно указывать не сумму длин полей, а полную длину структуры.
7.6 Понятие мультиплексирования. Мультиплексирование сообщений. Модель взаимодействия процессов клиент-сервер. Неравноправность клиента и сервера
Используя технику из предыдущего примера, можно организовать получение сообщений одним процессом от множества других процессов через одну очередь сообщений и отправку им ответов через ту же очередь сообщений, т. е. осуществить мультиплексирование сообщений. Вообще под мультиплексированием
179
информации понимают возможность одновременного обмена информацией с несколькими партнерами. Метод мультиплексирования широко применяется в модели взаимодействия процессов клиент-сервер. В этой модели один из процессов является сервером. Сервер получает запросы от других процессов – клиентов – на выполнение некоторых действий и отправляет им результаты обработки запросов. Чаще всего модель клиент-сервер используется при разработке сетевых приложений. Она изначально предполагает, что взаимодействующие процессы неравноправны:Сервер, как правило, работает постоянно, на всем протяжении жизни приложения, а клиенты могут работать эпизодически.
Сервер ждет запроса от клиентов, инициатором же взаимодействия является клиент.
Как правило, клиент обращается к одному серверу за раз, в то время как к серверу могут одновременно поступать запросы от нескольких клиентов.
Клиент должен знать, как обратиться к серверу (например, какого типа сообщения он воспринимает) перед началом организации запроса к серверу, в то время как сервер может получить недостающую информацию о клиенте из пришедшего запроса.
Пример схемы мультиплексирования сообщений через одну очередь сообщений для модели клиент-сервер.
Пусть сервер получает из очереди сообщений только сообщения с типом 1. В состав сообщений с типом 1, посылаемых серверу, процессы-клиенты включают значения своих идентификаторов процесса. Приняв сообщение с типом 1, сервер анализирует его содержание, выявляет идентификатор процесса, пославшего запрос, и отвечает клиенту, посылая сообщение с типом, равным идентификатору запрашивавшего процесса. Процесс-клиент после отправления запроса ожидает ответа в виде сообщения с типом, равным своему идентификатору. Поскольку идентификаторы процессов в системе различны, и ни один пользовательский процесс не может иметь PID, равный 1, все сообщения могут быть прочитаны только теми процессами, которым они адресованы. Если обработка запроса занимает продолжительное время, сервер может организовывать параллельную обработку запросов, порождая для каждого запроса новый процесс-ребенок или новую нить исполнения.
180