Книга: Операционная система UNIX

Сообщения

Сообщения

Как уже обсуждалось, очереди сообщений являются составной частью UNIX System V, они обслуживаются операционной системой, размещаются в адресном пространстве ядра и являются разделяемым системным ресурсом. Каждая очередь сообщений имеет свой уникальный идентификатор. Процессы могут записывать и считывать сообщения из различных очередей. Процесс, пославший сообщение в очередь, может не ожидать чтения этого сообщения каким-либо другим процессом. Он может закончить свое выполнение, оставив в очереди сообщение, которое будет прочитано другим процессом позже.

Данная возможность позволяет процессам обмениваться структурированными данными, имеющими следующие атрибуты:

? Тип сообщения (позволяет мультиплексировать сообщения в одной очереди)

? Длина данных сообщения в байтах (может быть нулевой)

? Собственно данные (если длина ненулевая, могут быть структурированными)

Очередь сообщений хранится в виде внутреннего однонаправленного связанного списка в адресном пространстве ядра. Для каждой очереди ядро создает заголовок очереди (msqid_ds), где содержится информация о правах доступа к очереди (msg_perm), ее текущем состоянии (msg_cbytes — число байтов и msg_qnum — число сообщений в очереди), а также указатели на первое (msg_first) и последнее (msg_last) сообщения, хранящиеся в виде связанного списка (рис. 3.18). Каждый элемент этого списка является отдельным сообщением.


Рис. 3.18. Структура очереди сообщений

Для создания новой очереди сообщений или для доступа к существующей используется системный вызов msgget(2):

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflag);

Функция возвращает дескриптор объекта-очереди, либо -1 в случае ошибки. Подобно файловому дескриптору, этот идентификатор используется процессом для работы с очередью сообщений. В частности, процесс может:

? Помещать в очередь сообщения с помощью функции msgsnd(2);

? Получать сообщения определенного типа из очереди с помощью функции msgrcv(2);

? Управлять сообщениями с помощью функции msgctl(2).

Перечисленные системные вызовы манипулирования сообщениями имеют следующий вид:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,
 size_t msgsz, int msgflg);
int msgrcv(int msqid, void *msgp,
 size_t msgsz, long msgtyp, int msgflg);

Здесь msgid является дескриптором объекта, полученного в результате вызова msgget(2). Параметр msgtyp указывает на буфер, содержащий тип сообщения и его данные, размер которого равен msgsz байт. Буфер имеет следующие поля:

long msgtype тип сообщения
char msgtext[] данные сообщения

Аргумент msgtyp указывает на тип сообщения и используется для их выборочного получения. Если msgtyp равен 0, функция msgrcv(2) получит первое сообщение из очереди. Если величина msgtyp выше 0, будет получено первое сообщение указанного типа. Если msgtyp меньше 0, функция msgrcv(2) получит сообщение с минимальным значением типа, меньше или равного абсолютному значению msgtyp.

Очереди сообщений обладают весьма полезным свойством — в одной очереди можно мультиплексировать сообщения от различных процессов. Для демультиплексирования используется атрибут msgtype, на основании которого любой процесс может фильтровать сообщения с помощью функции msgrcv(2) как это было показано выше.

Рассмотрим типичную ситуацию взаимодействия процессов, когда серверный процесс обменивается данными с несколькими клиентами. Свойство мультиплексирования позволяет использовать для такого обмена одну очередь сообщений. Для этого сообщениям, направляемым от любого из клиентов серверу, будем присваивать значение типа, скажем, равным 1. Если в теле сообщения клиент каким-либо образом идентифицирует себя (например, передает свой PID), то сервер сможет передать сообщение конкретному клиенту, присваивая тип сообщения равным этому идентификатору.

Поскольку функция msgrcv(2) позволяет принимать сообщения определенного типа (типов), сервер будет принимать сообщения с типом 1, а клиенты — сообщения с типами, равными идентификаторам их процессов. Схема такого взаимодействия представлена на рис. 3.19.


Рис. 3.19. Мультиплексирование сообщений в одной очереди

Атрибут msgtype также можно использовать для изменения порядка извлечения сообщений из очереди. Стандартный порядок получения сообщений аналогичен принципу FIFO — сообщения получаются в порядке их записи. Однако используя тип, например, для назначения приоритета сообщений, этот порядок легко изменить.

Пример приложения "Здравствуй, Мир!", использующего сообщения:

Файл описания mesg.h

#define MAXBUFF 80
#define PERM 0666
/* Определим структуру нашего сообщения. Она может отличаться
   от структуры msgbuf, но должна содержать поле mtype. В данном
   случае структура сообщения состоит из буфера обмена */
typedef struct our msgbuf {
 long mtype;
 char buff[MAXBUFF];
} Message;

Сервер:

#include <sys/ipc.h>
#include <sys/msg.h>
#include "mesg.h"
main() {
 /* Структура нашего сообщения (может отличаться от
    структуры msgbuf) */
 Message message;
 key_t key;
 int msgid, length, n;
 /* Получим ключ */
 if ((key = ftok("server", 'A')) < 0) {
  printf("Невозможно получить ключn");
  exit(1);
 }
 /* Тип принимаемых сообщений */
 message.mt_type = 1L;
 /* Создадим очередь сообщений */
 if ((msgid = msgget(key, РЕRМ | IPC_CREAT)) < 0) {
  printf("Невозможно создать очередьn");
  exit(1);
 }
 /* Прочитаем сообщение */
 n =
  msgrcv(msgid, &message, sizeof(message), message.mtype, 0);
 /* Если сообщение поступило, выведем его содержимое
    на терминал */
 if (n > 0) {
  if (write(1, message.buff, n) != n) {
   printf("Ошибка выводаn");
   exit(1);
  }
 } else {
  printf("Ошибка чтения сообщенияn");
  exit(1);
 }
 /* Удалить очередь поручим клиенту */
 exit(0);
}

Клиент:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "mesg.h"
main {
 /* Структура нашего сообщения (может отличаться от
    структуры msgbuf */
 Message message;
 key_t key;
 int msgid, length;
 /* Тип посылаемого сообщения, может использоваться для
    мультиплексирования */
 message.mtype = 1L;
 /* Получим ключ */
 if ((key = ftok("server", 'A')) < 0) {
  printf("Невозможно получить ключn");
  exit(1);
 }
 /* Получим доступ к очереди сообщений, очередь уже
    должна быть создана сервером */
 if ((msgid = msgget(key, 0)) < 0) {
  printf("Невозможно получить доступ к очередиn");
  exit(1);
 }
 /* Поместим строку в сообщение */
 if ((length = sprintf(message.buff,
  "Здравствуй, Мир!n")) < 0) {
  printf("Ошибка копирования в буферn");
  exit(1);
 } /* Передадим сообщение */
 if (msgsnd(msgid, (void*)&message, length, 0) != 0) {
  printf("Ошибка записи сообщения в очередьn");
  exit(1);
 }
 /* Удалим очередь сообщений */
 if (msgctl(msgid, IPC_RMID, 0) < 0) {
  printf("Ошибка удаления очередиn");
  exit(1);
 }
 exit(0);
}

Оглавление книги


Генерация: 0.046. Запросов К БД/Cache: 0 / 2
поделиться
Вверх Вниз