Уильям Стивенс - UNIX: взаимодействие процессов
Рис. 5.1. Очередь сообщений Posix, содержащая три сообщения
Мы предполагаем реализацию через связный список, причем его заголовок содержит два атрибута очереди: максимально допустимое количество сообщений в ней и максимальный размер сообщения. Об этих атрибутах мы расскажем более подробно в разделе 5.3.
В этой главе мы используем метод, к которому будем прибегать и в дальнейшем, рассматривая очереди сообщений, семафоры и разделяемую память. Поскольку все эти объекты IPC обладают по крайней мере живучестью ядра (вспомните раздел 1.3), мы можем писать небольшие программы, использующие эти методы для экспериментирования с ними и получения большей информации о том, как они работают. Например, мы можем написать программу, создающую очередь сообщений Posix, а потом написать другую программу, которая помещает сообщение в такую очередь, а потом еще одну, которая будет считывать сообщения из очереди. Помещая в очередь сообщения с различным приоритетом, мы увидим, в каком порядке они будут возвращаться функцией mq_receive.
5.2. Функции mq_open, mq_close, mq_unlink
Функция mq_open создает новую очередь сообщений либо открывает существующую:
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, …
/* mode_t mode, struct mq_attr *attr*/ );
/* Возвращает дескриптор очереди в случае успешного завершения;
–1 – в противном случае. */
Требования к аргументу пате описаны в разделе 2.2.
Аргумент oflag может принимать одно из следующих значений: O_RDONLY, O_WRONLY, O_RDWR в сочетании (логическое сложение) с O_CREAT, O_EXCL, O_NONBLOCK. Все эти флаги описаны в разделе 2.3.
При создании новой очереди (указан флаг O_CREAT и очередь сообщений еще не существует) требуется указание аргументов mode и attr. Возможные значения аргумента mode приведены в табл. 2.3. Аргумент attr позволяет задать некоторые атрибуты очереди. Если в качестве этого аргумента задать нулевой указатель, очередь будет создана с атрибутами по умолчанию. Эти атрибуты описаны в разделе 5.3.
Возвращаемое функцией mq_open значение называется дескриптором очереди сообщений, но оно не обязательно должно быть (и, скорее всего, не является) небольшим целым числом, как дескриптор файла или программного сокета. Это значение используется в качестве первого аргумента оставшихся семи функций для работы с очередями сообщений.
ПРИМЕЧАНИЕ
В системе Solaris 2.6 тип mqd_t определен как void*, а в Digital Unix 4.0B — как int. В нашем примере в разделе 5.8 эти дескрипторы трактуются как указатели на структуру. Название «дескриптор» было дано им по ошибке.
Открытая очередь сообщений закрывается функцией mq_close:
#include <mqueue.h>
int mq_close(mqd_t mqdes);
/*Возвращает 0 в случае успешного завершения. –1 в случае ошибки */
По действию эта функция аналогична close для открытого файла: вызвавший функцию процесс больше не может использовать дескриптор, но очередь сообщений из системы не удаляется. При завершении процесса все открытые очереди сообщений закрываются, как если бы для каждой был сделан вызов mq_close.
Для удаления из системы имени (пате), которое использовалось в качестве аргумента при вызове mq_open, нужно использовать функцию mq_unlink:
#include <mqueue.h>
int mq_unlink(const char *name);
/* Возвращает 0 в случае успешного завершения. –1 в случае ошибки */
Для очереди сообщений (как и для файла) ведется подсчет числа процессов, в которых она открыта в данный момент, и по действию эта функция аналогична unlink для файла: имя (пате) может быть удалено из системы, даже пока число подключений к очереди отлично от нуля, но удаление очереди (в отличие от удаления имени из системы) не будет осуществлено до того, как очередь будет закрыта последним использовавшим ее процессом.
Очереди сообщений Posix обладают по меньшей мере живучестью ядра (раздел 1.3), то есть они продолжают существовать, храня все имеющиеся в них сообщения, даже если нет процессов, в которых они были бы открыты. Очередь существует, пока она не будет удалена явно с помощью mq_unlink.
ПРИМЕЧАНИЕ
Мы увидим, что если очередь сообщений реализована через отображаемые в память файлы (раздел 12.2), она может обладать живучестью файловой системы, но это не является обязательным и рассчитывать на это нельзя.
Пример: программа mqcreate1
Поскольку очереди сообщений Posix обладают по крайней мере живучестью ядра, можно написать набор небольших программ для работы с ними — с этими программами будет проще экспериментировать. Программа из листинга 5.1[1] создает очередь сообщений, имя которой принимается в качестве аргумента командной строки.
Листинг 5.1. Создание очереди сообщений (указан флаг O_EXCL)//pxmsg/mqcreate1.с
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int с flags:
6 mqd_t mqd;
7 flags = O_RDWR | O_CREAT;
8 while ((c = Getopt(argc, argv, "e")) != –1) {
9 switch (c) {
10 case 'e':
11 flags |= O_EXCL;
12 break;
13 }
14 }
15 if (optind != argc – 1)
16 err_quit("usage: mqcreate [ –e ] <name>");
17 mqd = Mq_open(argv[optind], flags, FILE_MODE, NULL);
18 Mq_close(mqd);
19 exit(0);
20 }
В командной строке можно указать параметр –е, управляющий исключающим созданием очереди. (О функции getopt и нашей обертке Getopt рассказано более подробно в комментарии к листингу 5.5.) При возвращении функция getopt сохраняет в переменной optind индекс следующего аргумента, подлежащего обработке.
Мы вызываем функцию mq_open, указывая ей в качестве имени IPC полученный из командной строки параметр, не обращаясь к рассмотренной нами в разделе 2.2 функции px_ipc_name. Это даст нам возможность узнать, как в данной реализации обрабатываются имена Posix IPC (мы используем для этого наши маленькие тестовые программы на протяжении всей книги).
Ниже приведен результат работы программы в Solaris 2.6:
solaris % mqcreate1 /temp.1234 очередь успешно создается
solaris % ls -l /tmp/.*1234
-rw-rw-rw– 1 rstevens other1 132632 Oct 23 17:08 /tmp/.MQDtemp.1234
-rw-rw-rw– 1 rstevens other1 0 Oct 23 17:08 /tmp/.MQLtemp.1234
-rw-r--r-- 1 rstevens other1 0 Oct 23 17:08 /tmp/.MQPDtemp.1234
solaris % mqcreate1 –e /temp.1234 очередь уже создана
mq_open error for /temp.1234: File exists
Мы назвали эту версию программы mqcreate1, поскольку она будет улучшена в листинге 5.4, после того как мы обсудим использование атрибутов очереди. Разрешения на доступ к третьему файлу определяются константой FILE_MODE (чтение и запись для пользователя, только чтение для группы и прочих пользователей), но у двух первых файлов разрешения отличаются. Можно предположить, что в файле с буквой D в имени хранятся данные; файл с буквой L представляет собой какую-то блокировку, а в файле с буквой Р хранятся разрешения.
В Digital Unix 4.0B мы указываем действительное имя создаваемого файла:
alpha % mqcreate1 /tmp/myq.1234 очередь успешно создается
alpha % ls –l /tmp/myq.1234
-rw-r--r-- 1 rstevens system 11976 Oct 23 17:04 /tmp/myq.1234
alpha % mqcreate1 –e /tmp/myq.1234 очередь уже создана
mq_open error for /tmp/myq.1234: File exists
Пример: программа mqunlink
В листинге 5.2 приведена программа mqunlink, удаляющая из системы очередь сообщений.
Листинг 5.2. Удаление очереди из системы: mqunlink//pxmsg/mqunlink.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 if (argc != 2)
6 err_quit("usage: mqunlink <name>");
7 Mq_unlink(argv[1]);
8 exit(0);
9 }
С помощью этой программы мы можем удалить очередь сообщений, созданную программой mqcreate1:
solaris % mqunlink /temp.1234
При этом будут удалены все три файла из каталога /tmp, которые относятся к этой очереди.
5.3. Функции mq_getattr и mq_setattr
У каждой очереди сообщений имеются четыре атрибута, которые могут быть получены функцией mq_getattr и установлены (по отдельности) функцией mq_setattr:
#include <mqueue.h>
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr);
/* Обе функции возвращают 0 в случае успешного завершения; –1 – в случае возникновения ошибок */
Структура mq_attr хранит в себе эти четыре атрибута:
struct mq_attr {