Нейл Мэтью - Основы программирования в Linux
msgget
Очередь сообщений создается и предоставляет к себе доступ с помощью функции msgget:
int msgget(key_t key, int msgflg);
Программа должна предоставить значение параметра key, которое, как и в других средствах IPC, задает имя конкретной очереди сообщений. С помощью специального значения IPC_PRIVATE создается скрытая или частная очередь, которая теоретически доступна только текущему процессу. Как и в случае семафоров и совместно используемой памяти, в некоторых системах Linux такая очередь может не быть частной. Поскольку от скрытой или частной очереди очень мало пользы, это не слишком важная проблема. Как и раньше, второй параметр msgflg состоит из девяти флагов прав доступа. Для создания новой очереди сообщений специальный бит со значением IPC_CREAT должен быть объединен с правами доступа поразрядной операцией OR. Не считается ошибкой установка флага IPC_CREAT и задание ключа уже существующей очереди сообщений. Если очередь уже есть, флаг IPC_CREAT безмолвно игнорируется.
Функция msgget вернет положительное число, идентификатор очереди; в случае успешного завершения и -1 в случае сбоя.
msgsnd
Функция msgsnd позволяет добавить сообщение в очередь сообщений:
int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
Структура сообщения ограничена двумя способами. Во-первых, она должна быть меньше системного ограничения, и во-вторых, она должна начинаться с элемента типа long int, который будет использован как тип сообщения в получающей функции. Если вы применяете сообщения, лучше всего определить структуру сообщения следующим образом.
struct my_message {
long int message_type;
/* Данные, которые вы собираетесь передавать */
}
Поскольку элемент message_type используется при получении сообщения, вы не можете его просто игнорировать. Вы должны включить его в вашу структуру данных, и будет разумно инициализировать его с помощью известного значения.
Первый параметр msqid — идентификатор очереди сообщений, возвращаемый функцией msgget.
Второй параметр msg_ptr — указатель на отправляемое сообщение, которое должно начинаться с элемента типа long int, как описывалось ранее.
Третий параметр msg_sz — объем сообщения, на которое указывает msg_ptr. Этот объем не должен включать элемент типа long int, содержащий тип сообщения.
Четвертый параметр msgflg управляет действиями, предпринимаемыми при заполнении текущей очереди сообщений или достижении общесистемного ограничения для очередей сообщений. Если в параметре msgflg установлен флаг IPC_NOWAIT, функция вернет управление немедленно без отправки сообщения и возвращаемое значение будет равно -1. Если в параметре msgflg флаг IPC_NOWAIT сброшен, процесс отправки будет приостановлен в ожидании освобождения доступного объема в очереди.
В случае успеха функция вернет 0, а в случае аварийного завершения — -1. Если вызов был успешен, копия данных сообщения принимается и помещается в очередь сообщений.
msgrcv
Функция msgrcv извлекает сообщения из очереди сообщений:
int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);
Первый параметр msqid — идентификатор очереди сообщений, возвращенный функцией msgget.
Второй параметр msg_ptr — указатель на получаемое сообщение, которое должно начинаться с элемента типа long int, как описывалось ранее в функции msgsnd.
Третий параметр msg_sz — размер сообщения, на которое указывает msg_ptr, без элемента типа long int, содержащего тип сообщения.
Четвертый параметр msgtype типа long int позволяет реализовать простую форму приоритетного получения. Если значение msgtype равно 0, извлекается первое доступное сообщение в очереди. Если значение параметра больше нуля, извлекается первое сообщение с таким же типом сообщения. Если оно меньше нуля, извлекается первое сообщение с таким же типом сообщения или со значением, по абсолютной величине меньшим, чем msgtype.
На практике все гораздо проще. Если вы просто хотите получать сообщения в порядке их отправления, задайте msgtype, равным 0. Если нужно извлекать сообщения только с определенным типом, задайте msgtype, равным этому значению. Если вам необходимо получать сообщения с типом не превышающим n, задайте msgtype, равным -n.
Четвертый параметр msgflg управляет действиями в случае отсутствия сообщения подходящего типа, которое ожидает извлечения. Если в параметре msgflg установлен флаг IPC_NOWAIT, вызов вернет управление программе немедленно с возвращаемым значением -1. Если флаг IPC_NOWAIT в msgflg сброшен, процесс будет приостановлен в ожидании прибытия сообщения подходящего типа.
В случае успешного завершения функция msgrcv вернет количество байтов, помещенных в буфер приема, сообщение копируется в выделяемый пользователем буфер, на который указывает msg_ptr, и данные удаляются из очереди сообщений. В случае ошибки функция вернет -1.
msgctl
Последняя функция обработки очереди сообщений msgctl очень похожа на функцию управления для совместно используемой памяти:
int msgctl(int msqid; int command, struct msqid_ds *buf);
Структура msqid_ds содержит, как минимум, следующие элементы:
struct msqid_ds {
uid_t msg_perm.uid;
uid_t msg_perm.gid;
mode_t msg_perm.mode;
}
Первый параметр msqid — идентификатор, возвращаемый функцией msgget.
Второй параметр command задает предпринимаемое действие. Он может принимать три значения, перечисленные в табл. 14.3.
Таблица 14.3
Значение Описание IPC_STAT Задает данные в структуре msqid_ds, отображающие значения, связанные с очередью сообщений IPC_SET Если у процесса есть на это право, это действие устанавливает значения, связанные с очередью сообщений, в соответствии с данными структуры msqid_ds IPC_RMID Удаляет очередь сообщенийВ случае успешного завершения возвращает 0, в случае аварийного — -1. Если очередь сообщений удаляется, когда процесс ожидает в функции msgsnd или msgrcv, функция отправки или получения сообщения завершается аварийно.
Выполните упражнение 14.3.
Упражнение 14.3. Очереди сообщенийТеперь, когда вы познакомились с объявлениями, относящимися к очередям сообщений, можно посмотреть, как они действуют на практике. Как и раньше, вы напишите две программы: msg1.c для получения и msg2.c для отправки сообщений. Вы разрешите обеим программам создавать очередь сообщений, но используете для удаления очереди программу-приемник после того, как она получит последнее сообщение.
1. Далее приведена программа-приемник msg1 .с:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/msg.h>
struct my_msg_st {
long int my_msg_type;
char some_text[BUFSIZ];
};
int main() {
int running = 1;
int msgid;
struct my_msg_st some_data;
long int msg_to_receive = 0;
2. Прежде всего, задайте очередь сообщений:
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
if (msgid == -1) {
fprintf(stderr, "msgget failed with error: %dn", errno);
exit(EXIT_FAILURE);
}
3. Далее сообщения извлекаются из очереди до тех пор, пока не будет обнаружено сообщение end. В конце очередь сообщений удаляется.
while (running) {
if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1) {
fprintf(stderr, "msgrcv failed with error: %dn", errno);
exit(EXIT_FAILURE);
}
printf("You wrote: %s", some_data.some_text);
if (strncmp(some_data.some_text, "end", 3) == 0) {
running = 0;
}
}
if (msgctl(msgid, IPC_RMID, 0) == -1) {
fprintf(stderr, "msgctl(IPC_RMID) failedn");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
4. Программа-отправитель msg2.c очень похожа на программу msg1.с. В функции main удалите объявление msg_to_receive и замените его переменной buffer[BUFSIZ]. Уберите из программы удаление очереди и внесите следующие изменения в цикл с управляющей переменной running. Теперь у вас появился вызов функции msgsnd для отправки введенного текста в очередь сообщений. Далее приведена программа msg2.c с отличиями от программы msg1.с, выделенными цветом.