186
в дальнейшем для других операций с ней.
Здесь параметр key - ключ System V IPC для очереди сообщений.
Замечание
В качестве значения этого параметра может быть использовано значение ключа, полученное с помощью функции ftok(), или специальное значение IPC_PRIVATE, использование которого всегда приводит к попытке создания новой очереди сообщений с идентификатором, не совпадающим ни с одной из уже существующих очередей.
Параметр msgflg – имеет значение только при создании новой очереди сообщений и определяет права различных пользователей при доступе к очереди, а также необходимость создания новой очереди и поведение системного вызова при попытке создания. Он является некоторой комбинацией, с помощью операции побитовое или – " | ", следующих предопределенных значений и восьмеричных прав доступа:
•IPC_CREAT — если очереди для указанного ключа не существует, она должна быть создана;
•IPC_EXCL — применяется совместно с флагом IPC_CREAT. При совместном
их использовании и существовании массива с указанным ключом доступ к очереди не производится и констатируется ошибочная ситуация, при этом переменная errno, описанная в файле <errno.h>, примет значение EEXIST;
•0400 — разрешено чтение для пользователя, создавшего очередь;
•0200 — разрешена запись для пользователя, создавшего очередь;
•0040 — разрешено чтение для группы пользователя, создавшего очередь;
•0020 — разрешена запись для группы пользователя, создавшего очередь;
•0004 — разрешено чтение для всех остальных пользователей;
•0002 — разрешена запись для всех остальных пользователей.
Замечание
Очередь сообщений имеет ограничение по общему количеству хранимой информа-ции, которое может быть изменено администратором системы, а текущее значение ограничения можно узнать с помощью команды:
ipcs -l
Системный вызов msgget() возвращает:
•значение дескриптора System V IPC для очереди сообщений, при нормальном завершении;
•значение -1, при возникновении ошибки.
Системный вызов msgsnd() предназначен для помещения сообщения в очередь сообщений.
int msgsnd(int msqid, struct msgbuf *ptr, int length, int flag);
Параметр msqid является дескриптором System V IPC для очереди, в которую
187
отправляется сообщение.
Непосредственно, данные передаются структуры типа struct msgbuf, которая описана в файле <sys/msg.h> как
struct msgbuf { long mtype; char mtext[1];
};
где mtype - имеет тип long, интерпретируемый как тип сообщения; должен быть
строго положительной величиной.
mtext[...] - массив байт передаваемого сообщения; в Linux, назмер этого массива ограничен размером 4080 байт и может быть еще уменьшен системным администратором.
Например:
struct mymsgbuf { long mtype;
char mtext[1024]; } mybuf;
Более того, информация вовсе не обязана быть текстовой, например:
struct mymsgbuf { long mtype; struct {
int iinfo; float finfo;
}info;
}mybuf;
Параметр length — действительная длинна полезной информации, которая следует за типом сообщения, в структуре msgbuf.
Параметр flag может принимать два значения: 0 и IPC_NOWAIT:
•если значение флага равно 0, и в очереди не хватает места для того, чтобы
поместить сообщение, то системный вызов блокируется до тех пор, пока не освободится место;
•при значении флага IPC_NOWAIT системный вызов в этой ситуации не
блокируется, а констатируется возникновение ошибки с установлением значения переменной errno, описанной в файле <errno.h>, равным EAGAIN.
Системный вызов msgsnd() возвращает:
•значение 0, при нормальном завершении;
•значение -1, при возникновении ошибки.
Системный вызов msgrcv() предназначен для получения сообщения из очереди сообщений.
int msgrcv(int msqid, struct msgbuf *ptr, int length, long type, int flag);
188
где параметр msqid - дескриптор System V IPC очереди, из которой извлекается сообщение;
type - способ выборки сообщения из очереди:
•0 - в порядке FIFO, независимо от типа сообщения;
•n - в порядке FIFO, для сообщений с типом n;
•-n - первым выбирается сообщение с минимальным типом, не превышающим
значения n, пришедшее ранее всех других сообщений с тем же типом.
length - максимальная длина полезной части информации, расположенной в структуре после типа сообщения, которая может быть размещена в сообщении. Параметр flag может принимать значение 0 или быть какой-либо комбинацией флагов IPC_NOWAIT и MSG_NOERROR:
•Если флаг IPC_NOWAIT не установлен и очередь сообщений пуста или в ней
нет сообщений с заказанным типом, то системный вызов блокируется до появления запрошенного сообщения.
•При установлении флага IPC_NOWAIT системный вызов в этой ситуации не
блокируется, а констатирует возникновение ошибки с установлением значения переменной errno, описанной в файле <errno.h>, равным EAGAIN.
•Если действительная длина полезной части информации в выбранном
сообщении превышает значение, указанное в параметре length и флаг MSG_NOERROR не установлен, то выборка сообщения не производится, и фиксируется наличие ошибочной ситуации.
•Если флаг MSG_NOERROR установлен, то в этом случае ошибки не возникает, а сообщение копируется в сокращенном виде.
Вслучае удачи, системный вызов msgrcv() копирует выбранное сообщение из очереди сообщений по адресу, указанному в параметре ptr, одновременно удаляя его из очереди сообщений.
Системный вызов msgrcv() возвращает:
•действительную длину полезной части информации, при нормальном завершении;
•значение -1, при возникновении ошибки.
Системный вызов msgctl() предназначен для получения информации об очереди сообщений, изменения ее атрибутов и удаления из системы.
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
где msqid - дескриптор System V IPC, которое вернул системный вызов msgget() при создании очереди или при ее поиске по ключу;
cmd - используется как команда (см. руководство man).
Замечание
IPC_RMID – команда для удаления очереди сообщений с заданным идентификатором. Параметр buf для этой команды не используется, поэтому мы всегда будем подставлять туда значение NULL.
189
Системный вызов msgctl() возвращает:
•значение 0, при нормальном завершении;
•значение -1, при возникновении ошибки.
Рассморим практическое применение очередей сообщений, на примере работы двух программ:
•программа lab11.5, показанная на листинге 5.5, пишет в очередь ряд сообщений с типом 0 и заканчивает запись в очередь сообщением с типом 255;
•программа lab11.6, показанная на листинге 5.6, читает из очереди все сооб-
щения и печатает их; чтение сообщений заканчивается, когда получено сообщение с типом 255;
Листринг 5.5 - Текст программы, которая пишет сообщения в очередь
/** Программа lab11.5, иллюстрирующая запись очереди сообщений.
*
*она получает доступ к очереди сообщений, отправляет в нее 5 текстовых сообщений
*с типом 1 и одно пустое сообщение с типом 255,
*которое будет служить для программы lab11.5
*сигналом прекращения работы. */
#include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #include <stdio.h>
/* Тип сообщения для прекращения работы программы lab11.6 */ #define LAST_MESSAGE 255
int main()
{
int msqid; // IPC дескриптор для очереди сообщений
//Имя файла, использующееся для генерации ключа.
//Файл с таким именем должен существовать
char pathname[] = "/home/upk/lab11"; key_t key; // IPC ключ
int i,len; // Счетчик цикла и длина информативной части сообщения
/* Пользовательская структура для сообщения */ struct mymsgbuf
{
long mtype; char mtext[81];
} mybuf;
/* Массив сообщений */ char mesg[5][81] = {
"Сообщение один.", "Сообщение два.", "Сообщение три.", "Сообщение четыре.", "Сообщение пять."
};
printf("lab11.5: Начинаю работу...\n");
// Генерируем IPC ключ из имени файла и номера экземпляра очереди сообщений 0.
190
if((key = ftok(pathname,0)) < 0){ printf("Не могу сгенерировать key\n"); exit(-1);
}
//Получаем доступ по ключу к очереди сообщений, если она существует,
//или создать ее, с правами доступа read & write для всех пользователей if((msqid = msgget(key, 0666 | IPC_CREAT)) < 0){
printf("Не могу получить msqid\n"); exit(-1);
}
// Посылаем в цикле 5 сообщений с типом 1 в очередь сообщений. for (i = 0; i < 5; i++){
/* Сначала заполняем структуру для нашего сообщения и определяем длину информативной части */ mybuf.mtype = 1;
strcpy(mybuf.mtext, mesg[i]); len = strlen(mybuf.mtext)+1;
//Отсылаем сообщение. В случае ошибки сообщаем об
//этом и удаляем очередь сообщений из системы.
if (msgsnd(msqid, (struct msgbuf *) &mybuf, len, 0) < 0){ printf("Не могу послать сообщение в очередь\n"); msgctl(msqid, IPC_RMID,
(struct msqid_ds *) NULL); exit(-1);
}
printf("Записал в очередь: %s\n", mesg[i]);
}
//Отсылаем сообщение, которое заставит получающий процесс
//прекратить работу, с типом LAST_MESSAGE и длиной 0 mybuf.mtype = LAST_MESSAGE;
len = 0;
if (msgsnd(msqid, (struct msgbuf *) &mybuf, len, 0) < 0){
printf("Не могу послать сообщение в очередь\n"); msgctl(msqid, IPC_RMID, (struct msqid_ds *) NULL); exit(-1);
}
printf("Записал в очередь: пустое сообщение\n"); printf("lab11.5: Закончила работу...\n"); return 0;
}
На рисунке 5.1 показан вывод этой программы.
Рисунок 5.1 — Вывод программы пишущей сообщения