Уильям Стивенс - UNIX: взаимодействие процессов
#define MY_DATA 8
typedef struct my_msgbuf {
long mtype; /* тип сообщения */
int16_t mshort; /* начало данных */
char mchar[MY_DATA];
} Message;
Аргумент length функции msgsnd указывает длину сообщения в байтах. Это длина пользовательских данных, следующих за типом сообщения (целое типа long). Длина может быть и 0. В указанном выше примере длина может быть вычислена как sizeof(Message) – sizeof(long).
Аргумент flag может быть либо 0, либо IPC_NOWAIT. В последнем случае он отключает блокировку для msgsnd: если для нового сообщения недостаточно места в очереди, возврат из функции происходит немедленно. Это может произойти, если:
■ в данной очереди уже имеется слишком много данных (значение msg_qbytes в структуре msqid_ds);
■ во всей системе имеется слишком много сообщений.
Если верно одно из этих условий и установлен флаг IPC_NOWAIT, функция msgsnd возвращает ошибку с кодом EAGAIN. Если флаг IPC_NOWAIT не указан, а одно из этих условий выполняется, поток приостанавливается до тех пор, пока не произойдет одно из следующего:
■ для сообщения освободится достаточно места;
■ очередь с идентификатором msqid будет удалена из системы (в этом случае возвращается ошибка с кодом EIDRM);
■ вызвавший функцию поток будет прерван перехватываемым сигналом (в этом случае возвращается ошибка с кодом EINTR).
6.4. Функция msgrcv
Сообщение может быть считано из очереди с помощью функции msgrcv.
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t length, long type, int flag);
/* Возвращает количество данных в сообщении, –1 – в случае ошибки */
Аргумент ptr указывает, куда следует помещать принимаемые данные. Как и для msgsnd, он указывает на поле данных типа long (рис. 4.13), которое непосредственно предшествует полезным данным.
Аргумент length задает размер относящейся к полезным данным части буфера, на который указывает ptr. Это максимальное количество данных, которое может быть возвращено функцией. Поле типа long не входит в эту длину.
Аргумент type определяет тип сообщения, которое нужно считать из очереди:
■ если значение type равно 0, возвращается первое сообщение в очереди (то есть при указании типа 0 возвращается старейшее сообщение);
■ если тип больше 0, возвращается первое сообщение, тип которого равен указанному;
■ если тип меньше нуля, возвращается первое сообщение с наименьшим типом, значение которого меньше либо равно модулю аргумента type.
Рассмотрим пример очереди сообщений, изображенный на рис. 6.1. В этой очереди имеются три сообщения:
■ первое сообщение имеет тип 100 и длину 1;
■ второе сообщение имеет тип 200 и длину 2;
■ третье сообщение имеет тип 300 и длину 3.
Таблица 6.1 показывает, какое сообщение будет возвращено при различных значениях аргумента type.
Таблица 6.1. Возвращаемое сообщение в зависимости от аргумента type
type Тип возвращаемого сообщения 0 100 100 100 200 200 300 300 -100 100 -200 100 -300 100Аргумент flag указывает, что делать, если в очереди нет сообщения с запрошенным типом. Если установлен бит IPC_NOWAIT, происходит немедленный возврат из функции msgrcv с кодом ошибки ENOMSG. В противном случае вызвавший процесс блокируется до тех пор, пока не произойдет одно из следующего:
■ появится сообщение с запрошенным типом;
■ очередь с идентификатором msqid будет удалена из системы (в этом случае будет возвращена ошибка с кодом EIDRM);
■ вызвавший поток будет прерван перехватываемым сигналом (в этом случае возвращается ошибка EINTR).
В аргументе flag можно указать дополнительный бит MSG_NOERROR. При установке этого бита данные, превышающие объем буфера (аргумент length), будут просто обрезаться до его размера без возвращения кода ошибки. Если этот флаг не указать, при превышении объемом сообщения аргумента length будет возвращена ошибка E2BIG.
В случае успешного завершения работы msgrcv возвращает количество байтов в принятом сообщении. Оно не включает байты, нужные для хранения типа сообщения (long), который также возвращается через указатель ptr.
6.5. Функция msgctl
Функция msgctl позволяет управлять очередями сообщений:
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buff);
/* Возвращает 0 в случае успешного завершения, –1 в случае ошибки */
Команд (аргумент cmd) может быть три:
■ IPC_RMID — удаление очереди с идентификатором msqidиз системы. Все сообщения, имеющиеся в этой очереди, будут утеряны. Мы уже видели пример действия этой функции в листинге 3.2. Для этой команды третий аргумент функции игнорируется.
■ IPC_SET — установка значений четырех полей структуры msqid_ds данной очереди равными значениям соответствующих полей структуры, на которую указывает аргумент buff: msg_perm.uid, msg_perm.gid, msg_perm.mode, msg_qbytes.
■ IPC_STAT — возвращает вызвавшему процессу (через аргумент buff) текущее содержимое структуры msqid_ds для указанной очереди.
Пример
Программа в листинге 6.1 создает очередь сообщений, помещает в нее сообщение с 1 байтом информации, вызывает функцию msgctl с командой IPC_STAT, выполняет команду ipcs, используя функцию system, а затем удаляет очередь, вызвав функцию msgctl с командой IPC_RMID.
Листинг 6.1.[1] Пример использования функции msgctl с командой IPC_STAT//svmsg/ctl.с
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int msqid;
6 struct msqid_ds info;
7 struct msgbuf buf;
8 msqid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);
9 buf.mtype = 1;
10 buf.mtext[0] = 1;
11 Msgsnd(msqid, &buf, 1, 0);
12 Msgctl(msqid, IPC_STAT, &info);
13 printf("read-write: *03o, cbytes = %lu, qnum = %lu, qbytes = %lun",
14 info.msg_perm.mode & 0777, (ulong_t) info.msg_cbytes,
15 (ulong_t) info.msg_qnum, (ulong_t) info.msg_qbytes);
16 system("ipcs –q");
17 Msgctl(msqid, IPC_RMID, NULL);
18 exit(0);
19 }
Мы собираемся отправить сообщение размером 1 байт, поэтому можно просто воспользоваться стандартным определением структуры msgbuf из <sys/msg.h>. Выполнение этой программы приведет к следующему результату:
solaris %ctl
read-write: 664, cbytes = 1, qnum = 1, qbytes = 4096
IPC status from <running system> as of MOn Oct 20 15:36:49 1997
T ID Key MODE OWNER GROUP
Message Queues:
q 1150 00000000 –rw-rw-r-- rstevens other1
Выведенные значения соответствуют ожидаемым. Нулевое значение ключа обычно соответствует IPC_PRIVATE, как мы отмечали в разделе 3.2. В этой системе на очередь сообщений накладывается ограничение по объему в 4096 байт. Поскольку мы записали сообщение с 1 байтом данных и msg_cbytes имеет значение 1, это ограничение накладывается на объем полезных данных и не включает тип сообщения (long), указываемый для каждого сообщения.
6.6. Простые примеры
Поскольку очереди сообщений System V обладают живучестью ядра, мы можем написать несколько отдельных программ для работы с этими очередями и изучить их действие.
Программа msgcreate
В листинге 6.2 приведена программа msgcreate, создающая очередь сообщений.
9-12 Параметр командной строки –e позволяет указать флаг IPC_EXCL.
16 Полное имя файла, являющееся обязательным аргументом командной строки, передается функции ftok. Получаемый ключ преобразуется в идентификатор функцией msgget.
Листинг 6.2. Создание очереди сообщений System V//svmsg/msgcreate.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int c, oflag, mqid;
6 oflag = SVMSG_MODE | IPC_CREAT;
7 while ((c = Getopt(argc, argv, "e")) != –1) {
8 switch (c) {
9 case 'e':
10 oflag |= IPC_EXCL;
11 break;
12 }
13 }
14 if (optind != argc – 1)