Уильям Стивенс - UNIX: взаимодействие процессов
struct sigaction {
void (*sa_handler)(); /* SIG_DFL, SIG_IGN или адрес обработчика сигнала */
sigset_t sa_mask; /* дополнительные блокируемые сигналы */
int sa_flags; /* параметры сигналов: SA_XXX */
void (*sa_sigaction)(int, siginfo_t, void *);
};
Правила действуют следующие:
■ Если в поле sa_flags установлен флаг SA_SIGINFO, поле sa_sigaction указывает адрес функции-обработчика сигнала.
■ Если флаг SA_SIGINFO не установлен, поле sa_handler указывает адрес функции-обработчика сигнала.
■ Чтобы сопоставить сигналу действие по умолчанию или игнорировать его, следует установить sa_handler равным либо SIG_DFL, либо SIG_IGN и не устанавливать флаг SA_SIGINFO.
Установка SA_SIGINFO8-17 Мы всегда устанавливаем флаг SA_SIGINFO и указываем флаг SA_RESTART, если перехвачен какой-либо другой сигнал, кроме SIGALRM.
5.8. Реализация с использованием отображения в память
Теперь рассмотрим реализацию очередей сообщений Posix с использованием отображения в память, взаимных исключений и условных переменных Posix.
ПРИМЕЧАНИЕ
Взаимные исключения и условные переменные описаны в главе 7, а ввод-вывод с отображением в память — в главах 12 и 13. Вы можете отложить данный раздел до ознакомления с этими главами.
На рис. 5.2 приведена схема структур данных, которыми мы пользуемся для реализации очереди сообщений Posix. Изображенная очередь может содержать до четырех сообщений по 7 байт каждое.
В листинге 5.16 приведен заголовочный файл mqueue.h, определяющий основные структуры, используемые в этой реализации.
Тип mqd_tДескриптор нашей очереди сообщений является просто указателем на структуру mq_infо. Каждый вызов mq_open выделяет память под одну такую структуру, а вызвавшему возвращается указатель на нее. Повторим, что дескриптор очереди сообщений не обязательно является небольшим целым числом, как дескриптор файла — единственное ограничение, накладываемое Posix, заключается в том, что этот тип данных не может быть массивом.
Листинг 5.16. Заголовочный файл mqueue.h//my_pxmsg_mmap/mqueue.h
1 typedef struct mymq_info *mymqd_t;
2 struct mymq_attr {
3 long mq_flags; /* флаг очереди : O_NONBLOCK */
4 long mq_maxmsg; /* максимальное количество сообщений в очереди */
5 long mq_msgsize; /* максимальный размер сообщения в байтах */
6 long mq_curmsgs; /* количество сообщений в очереди */
7 };
8 /* одна структура mymq_hdr{} на очередь, в начале отображаемого файла */
9 struct mymq_hdr {
10 struct mymq_attr mqh_attr; /* атрибуты очереди */
11 long mqh_head; /* индекс первого сообщения*/
12 long mqh_free; /* индекс первого пустого сообщения */
13 long mqh_nwait; /* количество заблокированных mq_receive() потоков */
14 pid_t mqh_pid; /* ненулевой PID. если включено уведомление */
15 struct sigevent mqh_event; /* для mq_notify() */
16 pthread_mutex_t mqh_lock; /* блокировка: mutex*/
17 pthread_cond_t mqh_wait; /* и условная переменная */
18 };
19 /* один mymsg_hdr{} в начале каждого сообщения */
20 struct mymsg_hdr {
21 long msg_next; /* индекс следующего сообщения в списке */
22 /* msg_next должно быть первым полем в структуре */
23 ssize_t msg_len; /* реальная длина */
24 unsigned int msg_prio; /* приоритет */
25 };
26 /* одна mymq_info{} выделяется при каждом mq_open() */
27 struct mymq_info {
28 struct mymq_hdr *mqi_hdr; /* начало отображаемого файла */
29 long mqi_magic; /* магическое значение после инициализации */
30 int mqi_flags; /* флаги для данного процесса */
31 };
32 #define MQI_MAGIC 0x98765432
33 /* размер сообщения округляется для подгонки */
34 #define MSGSIZE(i) ((((i) + sizeof(long)-1) / sizeof(long)) * sizeof(long))
Рис. 5.2. Схема структур данных, используемых при реализации очередей сообщений posix через отображаемый в память файл
Структура mq_hdr8-18 Эта структура хранится в самом начале отображаемого файла и содержит всю информацию об очереди. Поле mq_flags структуры mqh_attr не используется, поскольку флаги (единственный определенный флаг используется для отключения блокировки) должны обрабатываться для каждого открывающего очередь процесса в отдельности, а не для очереди в целом. Поэтому флаги хранятся в структуре mq_info. О прочих полях этой структуры мы будем говорить в связи с их использованием различными функциями.
Обратите внимание, что все переменные, называемые нами индексными (поля этой структуры mqh_head и mqh_free, а также поле msg_next следующей структуры), содержат индексы байтов относительно начала отображаемого в память файла. Например, размер структуры mq_hdr в системе Solaris 2.6 — 96 байт, поэтому индекс первого сообщения, располагающегося сразу за заголовком, имеет значение 96. Каждое сообщение на рис. 5.2 занимает 20 байт (12 байт на структуру msg_hdr и 8 байт на данные), поэтому индексы следующих трех сообщений имеют значения 116, 136 и 156, а размер отображаемого в память файла — 176 байт. Индексы используются для обработки двух связных списков, хранящихся в этом файле: в одном из списков (mqh_head) хранятся все сообщения, имеющиеся в данный момент в очереди, а в другом (mqh_free) — все незаполненные сообщения. Мы не можем использовать настоящие указатели на области памяти (адреса) при работе со списком, поскольку отображаемый файл может находиться в произвольной области памяти для каждого из процессов, работающих с ним (как показано в листинге 13.5).
Структура msg_hdr19-25 Эта структура располагается в начале каждого сообщения в отображаемом файле. Любое сообщение может принадлежать либо к списку заполненных, либо к списку свободных сообщений, и поле msg_next содержит индекс следующего сообщения в этом списке (или 0, если сообщение является в этом списке последним). Переменная msg_len хранит реальную длину данных в сообщении, которая в нашем примере с рис. 5.2 может иметь значение от 0 до 7 байт включительно. В переменную msg_prio отправителем помещается значение приоритета сообщения.
Структура mq_info26-32 Экземпляр такой структуры динамически создается функцией mq_open при открытии очереди и удаляется mq_close. Поле mqi_hdr указывает на отображаемый файл (адрес начала файла возвращается mmap). Указатель на эту структуру имеет основной в нашей реализации тип mqd_t, он принимает значение, возвращаемое mq_open.
Поле mqi_magiс принимает значение MQI_MAGIC в момент инициализации структуры. Это значение проверяется всеми функциями, которым передается указатель типа mqd_t, что дает им возможность удостовериться, что указатель действительно указывает на структуру типа mq_infо. mqi_flags содержит флаг отключения блокировки для открывшего очередь процесса.
Макрос MSGSIZE33-34 В целях выравнивания содержимого файла (alignment) мы располагаем начало каждого сообщения так, чтобы его индекс был кратен размеру длинного целого. Следовательно, если максимальный размер сообщения не допускает такого выравнивания, мы добавляем к нему от 1 до 3 байт, как показано на рис. 5.2. При этом предполагается, что размер длинного целого — 4 байт (что верно для Solaris 2.6). Если размер длинного целого 8 байт (в Digital Unix 4.0B), нам придется добавлять к каждому сообщению от 1 до 7 байт.
Функция mq_open
В листинге 5.17 приведен текст первой части функции mq_open, создающей новую очередь сообщений или открывающей существующую.
Листинг 5.17. Функция mq_open: первая часть//my_pxmsg._mmap/mq_open. с
1 #include "unpipc.h"
2 #include "mqueue.h"
3 #include <stdarg.h>
4 #define MAX_TRIES 10
5 struct mymq_attr defattr =
6 { 0, 128, 1024, 0 };
7 mymqd_t
8 mymq_open(const char *pathname, int oflag, …)
9 {
10 int i, fd, nonblock, created, save_errno;
11 long msgsize, filesize, index;
12 va_list ap;
13 mode_t mode;
14 int8_t *mptr;
15 struct stat statbuff;
16 struct mymq_hdr *mqhdr;
17 struct mymsg_hdr *msghdr;
18 struct mymq_attr *attr;
19 struct mymq_info *mqinfo;
20 pthread_mutexattr_t mattr;
21 pthread_condattr_t cattr;
22 created = 0;
23 nonblock = oflag & O_NONBLOCK;
24 oflag &= ~O_NONBLOCK;
25 mptr = (int8_t *) MAP_FAILED;
26 mqinfo = NULL;
27 again:
28 if (oflag & O_CREAT) {
29 va_start(ap, oflag); /* ар инициализируется последним аргументом */
30 mode = va_arg(ap, va_mode_t) & ~S_IXUSR;
31 attr = va_arg(ap, struct mymq_attr *);
32 va_end(ap);
33 /* открытие с установкой бита user-execute */
34 fd = open (pathname, oflag | O_EXCL | O_RDWR, mode | S_IXUSR);