41
if ((index = mqhdr->mqh_head) == 0)
42 err_dump("mymq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
43 msghdr = (struct mymsg_hdr *) &mptr[index];
44 mqhdr->mqh_head = msghdr->msg_next; /* новое начало списка */
45 len = msghdr->msg_len;
46 memcpy(ptr, msghdr + 1, len); /* копирование самого сообщения */
47 if (priop != NULL)
48 *priop = msghdr->msg_prio;
49 /* только что считанное сообщение становится первым в списке пустых */
50 msghdr->msg_next = mqhdr->mqr_free;
51 mqhdr->mqh_free = index;
52 /* запуск любого процесса, заблокированного в вызове mq_send */
53 if (attr->mq_curmsgs == attr->mq_maxmsg)
54 pthread_cond_signal(&mqhdr->mqh_wait);
55 attr->mq_curmsgs--;
56 pthread_mutex_unlock(&mqhdr->mqh_lock);
57 return(len);
58 err:
59 pthread_mutex_unlock(&mqhdr->mqh_lock);
60 return(-1);
61 }
43-51 msghdr указывает на msg_hdr первого сообщения в очереди, которое мы и возвратим. Освободившееся сообщение становится первым в списке свободных.
52-54 Если очередь была полной в момент считывания сообщения, мы вызываем pthread_cond_signal для отправки сообщения любому из процессов, заблокированных в вызове mq_send.
5.9. Резюме
Очереди сообщений Posix просты в использовании: новая очередь создается (или существующая открывается) функцией mq_open; закрываются очереди вызовом mq_close, а удаляются mq_unlink. Поместить сообщение в очередь можно функцией mq_send, а считать его оттуда можно с помощью mq_receive. Атрибуты очереди можно считать и установить с помощью функций mq_getattr и mq_setattr, а функция mq_notify позволяет зарегистрировать процесс на уведомление о помещении нового сообщения в пустую очередь. Каждое сообщение в очереди обладает приоритетом (небольшое целое число), и функция mq_receive всегда возвращает старейшее сообщение с наивысшим приоритетом.
Изучая mq_notify, мы познакомились с сигналами реального времени стандарта Posix, которые обладают номерами от SIGMIN до SIGMAX. При установке обработчика для этих сигналов с флагом SA_SIGINFO они будут помещаться в очередь, доставляться в порядке очереди и сопровождаться двумя дополнительными аргументами (при вызове обработчика).
Наконец, мы реализовали большую часть возможностей очереди сообщений Posix в приблизительно 500 строках кода на языке С, используя отображаемые в память файлы, взаимные исключения и условные переменные Posix. Эта реализация иллюстрирует обработку ситуации гонок при создании новой очереди; еще раз нам придется столкнуться с такой ситуацией в главе 10 при реализации семафоров Posix.
Упражнения
1. Говоря о листинге 5.4, мы отметили, что атрибут
2. Измените листинг 5.8 так, чтобы при получении сигнала не вызывалась функция mq_notify. Затем поместите в очередь два сообщения и убедитесь, что для второго из них сигнал порожден не будет. Почему?
3. Измените листинг 5.8 так, чтобы сообщение из очереди при получении сигнала не считывалось. Вместо этого просто вызовите mq_notify и напечатайте сообщение о получении сигнала. Затем отправьте два сообщения и убедитесь, что для второго из них сигнал не порождается. Почему?
4. Что произойдет, если мы уберем преобразование двух констант к целому типу в первом вызове printf в листинге 5.14?