Шрифт:
Интервал:
Закладка:
♦ msg_ctime
Время последнего изменения очереди.
♦ wwait и rwait
Указатели в очередь ожидания ядра, которые используются, когда очередь переполнена и процесс вынужден ждать из-за этого.
♦ msg_cbytes
Суммарный объем всех сообщений в очереди.
♦ msg_qnum
Количество сообщений в очереди.
♦ msg_qbytes
Максимальный размер очереди.
♦ msg_lspid
PID процесса, который послал последнее сообщение в очереди.
♦ msg_lrpid
PID процесса, который получил сообщение из очереди.
26.5.2. Создание очереди сообщений
Для создания очереди сообщений используется системный вызов msgget(). Этот же вызов используется для подключения к уже существующей очереди:
int msgget(key_t key, int msgflg);
Первый аргумент — это ключ, который мы получаем с помощью системного вызова ftok(). Второй аргумент — это режим доступа к очереди:
♦ IPC_CREAT — создать очередь, если она не была создана ранее.
♦ IPC_EXCL — если использовать вместе с IPC_CREAT, то в случае, если очередь существует, мы получим ошибку.
Если использовать только IPC_CREAT (без IPC_EXCL), то вызов msgget() всегда возвращает идентификатор очереди, даже если очередь уже существует (происходит подключение к очереди). Если использовать IPC_EXCL вместе с IPC_CREAT, также будет создана новая очередь, но если очередь уже существует, подключения не произойдет, а функция msgget() возвратит -1 (ошибка).
Вместе с режимом IPC_CREAT можно указывать права доступа к очереди с помощью операции OR:
IPC_CREAT | 0660
Если произошла ошибка и msgget() вернул -1, то переменная errno устанавливается следующим образом:
♦ EACCESS — у вас нет прав доступа к объекту IPC;
♦ EEXIST — очередь уже существует, создание невозможно, но возможно подключение к очереди;
♦ EIDRM — очередь помечена для удаления;
♦ ENOENT — очередь не существует (в случае подключения);
♦ ENOMEM — не хватает памяти для создания очереди;
♦ ENOSPC — не хватает адресного пространства (то есть превышено максимальное количество очередей).
Следующий код создает очередь сообщений:
key_t key; /* ключ IPC */
int id; /* ID очереди сообщений */
/* создаем ключ */
key = ftok(".", 'd');
/* создаем очередь */
if ((id = msgget(key, IPC_CREAT | 0660 )) == -1) {
printf("Ошибка при создании очередиn");
}
26.5.3. Постановка сообщения в очередь
Для постановки сообщения в очередь используется вызов msgsnd():
int msgsnd(int msqid, struct msgbuf *msgp, int msgsz,
int msgflg);
Первый аргумент — это идентификатор очереди, в которую нужно добавить сообщение. Данный идентификатор мы предварительно получаем с помощью системного вызова msgget(). Второй параметр — это указатель на буфер сообщения. Третий аргумент — это длина сообщения без учета типа сообщения (4 байта). Последний аргумент обычно устанавливают равным 0 или IPC_NOWAIT, если вы не хотите, чтобы процесс был блокирован при постановке сообщения в очередь, в случае переполнения очереди. По умолчанию (когда флаг равен 0), если очередь переполнена, ваш процесс будет блокирован до тех пор, пока сообщение не будет поставлено в очередь.
Как обычно, в случае успеха вызов возвращает 0, а если произошла ошибка, то -1. С помощью errno можно анализировать ошибку:
♦ EAGAIN — очередь переполнена, а вы используете флаг IPC_NOWAIT, то есть сообщение будет удалено и вам нужно заново поставить его в очередь (отсюда и название ошибки — AGAIN (опять));
♦ EACCESS — у вас недостаточно прав для записи сообщения в очередь;
♦ EFAULT— неверный адрес буфера msgp (невозможно получить доступ к этому адресу);
♦ EIDRM — очередь сообщений удалена;
♦ EINVAL — ошибка в аргументах, например, неправильное значение идентификатора очереди, отрицательный тип сообщения, неправильный размер сообщения и т.д.
♦ ENOMEM — не хватает памяти.
Следующий фрагмент кода демонстрирует постановку сообщения в очередь;
int res, length; /* результат операции и длина сообщения */
struct my_buf *buf; /* само сообщение */
/* определяем длину сообщения — 4 байта */
length = sizeof(struct my_buf) - sizeof(long);
if ((res = msgsnd( id, &buf, length, 0)) == -1) {
printf("Ошибка при постановке сообщения в очередьn");
}
Наверное, вам уже не терпится увидеть реально работающий пример, а не куски кода, которые только отчасти связаны между собой. В листинге 26.5 представлена программа, создающая очередь сообщения и записывающая в нее сообщение.
Листинг 26.5. Пример работы с очередью
#include <stdio.h>
#include <stdlib.h>
#include <linux/ipc.h>
#include <linux/msg.h>
main() {
int id; /* Идентификатор очереди */
key_t key; /* Ключ */
int res, length; /* Результат операции и длина сообщения */
struct my_buf {
long mtype; /* тип сообщения */
/* Далее следуют произвольные поля -
они зависят от сообщения */
int op_type; /* тип операции */
int l_ор; /* первый операнд */
int r_op; /* второй операнд */
} msg;
/* Генерируем IPC-ключ */
key = ftok(".", 'd');
/* Создаем очередь или присоединяемся к уже существующей */
if ((id = msgget(key, IPC_CREAT | 0660)) == -1) {
printf("Ошибка при создании очередиn");
exit(1);
}
/* Заполняем сообщение */
msg.type = 1; /* тип сообщения,
должен быть положительным! */
msg.op_.type = 0; /* тип операции */
msg.l_op = 6;
msg.r_op = 5;
/* определяем длину сообщения - 4 байта */
length = sizeof(struct my_buf) - sizeof(long);
if ((res = msgsnd(id, &buf, length, 0)) == -1) {
printf("Ошибка при постановке сообщения в очередьn");
exit(1);
}
}
После запуска этой программы запустите программу ipcs и посмотрите на статус только что отправленного сообщения. Теперь напишем программу, которая получит это сообщение.
26.5.4. Получение сообщений очереди
Для получения сообщения используется системный вызов msgrcv():
int msgrcv(int msqid, struct msgbuf *msgp, int msgsz,
long mtype, int msgflg);
Первый аргумент определяет очередь, из которой нужно получить сообщение. Второй аргумент — это адрес буфера, в который будет записано сообщение. Третий аргумент — это ограничитель длины сообщения. Четвертый аргумент — это тип сообщения. Ядро будет искать в очереди наиболее старое сообщение данного типа и вернет его копию. Если mtype=0, то ядро вернет самое старое сообщение независимо от типа.
После успешного получения сообщения оно удаляется из очереди.
В случае успеха вызов msqrcv() возвращает число байтов, скопированных в буфер, или -1 в случае ошибки. Переменная errno устанавливается следующим образом:
♦ E2BIG — длина сообщения больше, чем ограничитель msgsz;
♦ EACCESS — у вас недостаточно прав;
♦ EFAULT — недоступен адрес буфера;
♦ EIDRM — очередь уничтожена ядром;
♦ EINTR — операция прервана поступившим сигналом;
♦ EINVAL — ошибка в аргументах, например, отрицательный размер сообщения или неверный номер очереди;
♦ ENOMSG — нет сообщения, удовлетворяющего условию. Посылается, если установлен флаг IPC_NOWAIT, в противном случае процесс будет ждать нужного сообщения.
Последний аргумент предоставляет дополнительные возможности по работе с сообщениями. Если установлен бит MSG_NOERROR в msgflg, то если размер сообщения больше, чем msgsz, оно будет обрезано и вы получите только msgsz байтов. Если флаг MSG_NOERROR не устанавливать, вы получите ошибку E2BIG.
Следующий код получает сообщение из очереди:
int id; /* ID очереди */
int res, length; /* результат операции и длина */
struct my_buf buf; /* буфер */
int type = 1; /* тип сообщения */
length = sizeof(struct my_buf) — sizeof(long);
if ((res =
msgrcv(id, &buf, length, type, 0)) == -1) {
printf("Ошибка!");
/* можно проанализировать ошибку */
if (errno==E2BIG) printf("Сообщение слишком большоеn");
if (errno==EACCESS) printf("Heт доступаn");
/* и т.д. */
exit(1);
}
26.5.5. Проверка наличия сообщения в очереди
Наверное, вы не хотите, чтобы ваша программа ждала, пока в очереди появится нужное сообщение. Используя особенности системного вызова msgrcv(), можно написать код проверки наличия сообщения определенного типа в очереди. Напишем функцию msg_exists(), которая будет возвращать TRUE, если сообщение есть в очереди, или FALSE, если сообщения в очереди нет.
- Операционная система UNIX - Андрей Робачевский - Программное обеспечение
- Разработка приложений в среде Linux. Второе издание - Майкл Джонсон - Программное обеспечение
- Искусство программирования для Unix - Эрик Реймонд - Программное обеспечение
- Linux - Алексей Стахнов - Программное обеспечение
- Fedora 8 Руководство пользователя - Денис Колисниченко - Программное обеспечение
- Недокументированные и малоизвестные возможности Windows XP - Роман Клименко - Программное обеспечение
- Изучаем Windows Vista. Начали! - Дмитрий Донцов - Программное обеспечение
- Windows Vista - Виталий Леонтьев - Программное обеспечение
- Архитектура операционной системы UNIX - Морис Бах - Программное обеспечение
- Windows Vista. Трюки и эффекты - Юрий Зозуля - Программное обеспечение