- •Введение
- •Глава 1. Фундаментальные концепции unix Систем
- •Программы, процессы и потоки
- •Сигналы
- •Идентификаторы процессов, группы процессов и сеансы
- •Система прав
- •Другие атрибуты процесса
- •Межпроцессное взаимодействие
- •Использование системных вызовов
- •Краткие описания функций и обработка ошибок
- •Контрольные вопросы
- •Литература
- •Глава 2. Базовые операции ввода-вывода
- •Файловые операции ввода - вывода
- •Стандартные дескрипторы
- •Системные вызовы open и creat
- •Системный вызов umask
- •Системный вызов unlink
- •Текущая позиция в файле
- •Системный вызов write
- •2.8. Системный вызов read
- •2.9. Системный вызов close
- •2.10. Системный вызов lseek
- •2.11. Системные вызовы pread и pwrite
- •2.12. Системные вызовы truncate и ftruncate
- •Контрольные вопросы
- •Литература
- •Глава 3. Дополнительные операции файлового ввода_вывода
- •Низкоуровневый доступ к файловой системе
- •Жесткие и символические ссылки
- •Системный вызов getcwd
- •Отображение метаданных файла
- •Системные вызовы getpwuid, getgrgid и getlogin
- •Каталоги
- •Системные вызовы chdir и fchdir
- •Системные вызовы mkdir и rmdir
- •Контрольные вопросы
- •Литература
- •Глава 4. Процессы и потоки
- •4.1. Среда окружения
- •Системный вызов exec
- •Системный вызов fork
- •Завершение процесса и системные вызовы exit
- •Системные вызовы wait, waitpid и waitid
- •Получение и изменение идентификаторов пользователя и группы
- •Получение и изменение приоритета
- •Контрольные вопросы
- •Литература
- •Глава 5. Механизмы межпроцессного взаимодействия
- •5.1. Каналы
- •5.2. Системные вызовы dup и dup2
- •5.3. Двунаправленное взаимодействие с использованием однонаправленных каналов
- •Контрольные вопросы
- •Литература
- •Глава 6.Механизмы взаимодействия процессов
- •Именованные каналы (fifo)
- •Системные вызовы для работы с очередями сообщений posix
- •Семафоры
- •Системные вызовы для работы с общей памятью posix
- •Контрольные вопросы
- •Литература
- •Глава 7.Сетевое взаимодействие и сокеты
- •Основные системные вызовы для работы с сокетами, образующими логические соединения
- •Обслуживание нескольких клиентов
- •Адресация сокетов
- •In_port_t sin_port; /* номер порта (uint16_t) */
- •In_addr_t s_addr; /* адрес iPv4 (uint32_t) */
- •Домен адресов af_inet6
- •In_port_t sin6_port; /* номер порта (uint16_t) */
- •Доменная система именования
- •Параметры сокетов
- •Контрольные вопросы
- •Литература
- •Глава 8.Сигналы и таймеры
- •Введение в сигналы
- •Жизненный цикл сигналов
- •Типы сигналов
- •Системный вызов sigaction
- •Контрольные вопросы
- •Литература
- •Заключение
- •Список литературы
- •Глава 2. Базовые операции ввода-вывода 14
- •Глава 3. Дополнительные операции файлового ввода_вывода 25
- •Глава 6. Механизмы взаимодействия процессов 58
Системные вызовы для работы с очередями сообщений posix
Существует два набора системных вызовов для работы с очередями сообщений, семафорами и общей памятью. Самый старый из них называется System V IPC, более современный - POSIX IPC. В этом разделе мы рассмотрим POSIX IPC. Проблема именования является скользким местом при работе с очередями сообщений POSIX. Сначала рассмотрим системный вызов mq_open, который открывает очередь сообщений:
mq_open - открывает очередь сообщений
#include <mqueue.h>
mqd_t mq_open (
const char *name, /* имя POSIX IPC */
int flags /* флаги (за исключением O_CREAT) */
);
/* Возвращает дескриптор очереди или -1 в случае ошибки (код ошибки - в errno) */
mqd_t mq_open (
const char *name, /* имя POSIX IPC */
int flags, /* флаги (включая O_CREAT) */
mode_t perms, /* права доступа */
struct mq_attr *attr /* атрибуты или NULL */
);
/* Возвращает дескриптор очереди или -1 в случае ошибки (код ошибки - в errno) */
struct mq_attr - структура для использования в вызовах mq_open, mq_getattr и mq_setattr
struct mq_attr {
long mq_flags; /* флаги */
long mq_maxmsg; /* максимальное число сообщений */
long mq_msgsize; /* максимальный размер одного сообщения */
long mq_curmsgs; /* количество сообщений в очереди */
}
Аргумент flags может быть собран с помощью тех же макросов, что и для вызова open (например, O_CREAT):
в зависимости от того, как будет использоваться очередь, можно использовать один из макросов: O_RDONLY, O_WRONLY или O_RDWR;
флаг O_CREAT используется для создания новой очереди, если таковой не существует. В этом случае используется вторая форма обращения к системному вызову;
с флагом O_EXCL новая очередь создается только тогда, когда она не существует. В противном случае будет возвращен признак ошибки;
флаг O_NONBLOCK создает очередь без возможности блокировки. Это означает, что mq_send и mq_receive будут возвращать признак ошибки с кодом EAGAIN, если очередь переполнена или пуста. По умолчанию очередь создается с возможностью блокировки.
Право на чтение и запись означает возможность извлекать сообщения из очереди и помещать их в очередь соответственно. Как и файлы, очереди имеют идентификаторы владельца и группы, они устанавливаются в соответствии с действующими идентификаторами процесса, создавшего очередь. При создании новой очереди с флагом O_CREAT, если аргумент attr не является пустым указателем, очереди присваиваются два первых атрибута из структуры mq_attr. Эти атрибуты устанавливают максимальное число сообщений в очереди и максимальный размер одного сообщения, которое может быть помещено в очередь. В большинстве реализаций очередь сообщений представляет собой связанный список, поэтому на момент вызова mq_open исчерпание свободного пространства не возникает, так как, невзирая на величину атрибутов, очередь фактически пуста. Если в аргументе attr передается пустой указатель, атрибуты очереди устанавливаются в значения по умолчанию, характерные для той или иной системы.
Закрывается очередь обращением к системному вызову mq_close:
mq_close - закрывает очередь сообщений
#include <mqueue.h>
int mq_close (
mqd_t mqd /* дескриптор очереди */
);
/* Возвращает 0 в случае успеха, -1 в случае ошибки (код ошибки - в errno) */
Очереди сообщений POSIX остаются существовать в системе до тех пор, пока не произойдет перезагрузка системы или пока не будет произведено обращение к системному вызову mq_unlink:
mq_unlink - удаляет очередь сообщений
#include <mqueue.h>
int mq_unlink (
const char *name /* имя POSIX IPC */
);
/* Возвращает 0 в случае успеха, -1 в случае ошибки (код ошибки - в errno) */
Системный вызов mq_unlink делает имя очереди недоступным сразу же, но фактическое ее разрушение откладывает до того момента, когда будет закрыт последний ссылающийся на нее дескриптор.
Следующий системный вызов mq_send дает возможность послать сообщение:
mq_send - помещает сообщение в очередь
#include <mqueue.h>
int mq_ send(
mqd_t mqd, /* дескриптор очереди */
const char *msg, /* сообщение */
size_t msgsize, /* размер сообщения */
unsigned priority /* приоритет */
);
/* Возвращает 0 в случае успеха, -1 в случае ошибки (код ошибки - в errno) */
Системный вызов mq_send помещает сообщение размером msgsize в очередь впереди сообщений с более низким приоритетом, но позади сообщений с тем же самым приоритетом. Вызов mq_send может быть заблокирован, если очередь заполнена до отказа, разумеется, при условии, что флаг O_NONBLOCK сброшен. Прием сообщения производится системным вызовом mq_receive:
mq_receive - извлекает сообщение из очереди
#include <mqueue.h>
ssize_t mq_receive (
mqd_t mqd, /* дескриптор очереди сообщений*/
char *msg, /* буфер для сообщения */
size_t msgsize, /* размер буфера */
unsigned *priorityp /* приоритет принятого сообщения или NULL*/
);
/* Возвращает размер сообщения или -1 в случае ошибки (код ошибки - в errno) */
Аргумент msgsize определяет размер буфера, на который указывает аргумент msg. По адресу priority записывается приоритет принятого сообщения. Полученное сообщение удаляется из очереди, возможность просмотреть сообщение без его извлечения не предусмотрена. Если очередь пуста, mq_receive блокируется при условии, что флаг O_NONBLOCK сброшен.
Работать с несколькими очередями или с очередью и еще чем-нибудь, что может привести к блокировке в системном вызове, очень неудобно, поскольку не имеется возможности выполнить проверку дескриптора очереди с помощью вызова select или poll. Однако можно заставить систему извещать приложение о прибытии нового сообщения посредством сигналов. Сделать это можно с помощью системного вызова mq_notify:
mq_ notify - включает или выключает режим асинхронных уведомлений
#include <mqueue.h>
int mq_ notify (
mqd_t mqd, /* дескриптор очереди */
const struct sigevent *ep /* извещение */
);
/* Возвращает 0 в случае успеха, -1 в случае ошибки (код ошибки - в errno) */
Когда процесс или поток включает режим уведомлений, они будут получать сигнал в момент поступления сообщения в пустую очередь mqd, при условии, что к этому моменту процесс или поток не будут заблокированы в mq_receive - в этом случае произойдет выход из заблокированного mq_receive. Только один процесс или поток могут подписаться на получение уведомлений. Попытка включить режим уведомления из второго процесса или потока потерпит неудачу. Режим уведомлений отключается, когда процесс или поток получают сигнал или когда они обращаются к системному вызову mq_ notify с аргументом sigevent равным NULL.
При поступлении сообщения в непустую очередь уведомление процесса не производится, таким образом, mq_ notify не гарантирует отправку сигнала по прибытии каждого сообщения. Например, первое сообщение, помещенное, в пустую очередь, вызовет передачу сигнала процессу, но если до извлечения первого сообщения в очередь поступит второе, то уведомление передано не будет, так как фактически в этот момент очередь не пуста. Таким образом, действия приложения должны быть следующими:
после вызова mq_ notify следует извлечь (вызовом mq_receive) все сообщения из очереди, с установленным флагом O_NONBLOCK;
после прибытия сигнала необходимо сразу же повторно подписаться на уведомления вызовом mq_ notify и выполнить действия, предусмотренные на предыдущем шаге.
Тип сигнала и другие характеристики уведомления определяются исходя из содержимого структуры sigevent.