Назад

Содержание

Вперед


1.7.3.1. Очереди сообщений.

    Очередь сообщений представляет собой механизм передачи порций данных одинаковой длины. Каждая создаваемая очередь имеет уникальный ключ.

    Создание объекта или доступ к нему производится при помощи функции msgget( ):

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget (key_t key, int flag);

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

MSG_R 00400 владелец может читать;
MSG_W 00200 владелец может писать;
(MSG_R>>3) 00040 пользователи, входящие в одну группу с владельцем (группа), могут читать;
(MSG_R>>3) 00020 группа может писать;
(MSG_R>>6) 00004 все остальные пользователи (остальные) могут читать;
(MSG_R>>6) 00002 остальные могут писать;
IPC_PRIVATE   создается объект, который может использоваться лишь данным процессом;
IPC_CREAT   создать объект, если он не существует;
IPC_EXCL   используется в сочетании с IPC_CREAT; если этот флаг выставлен, и объект с заданным ключом существует, то функция возвращает код ошибки.

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

    Сообщение представляет собой массив байт, первые четыре из которых являются целым числом, содержащим тип сообщения. Следующие далее байты содержат данные сообщения. Если перед тем как использовать сообщение в программе, можно определить его максимальный размер, целесообразно использовать следующую структуру:

struct msg_struct
{
  long msg_type;                 /* тип сообщения */
  char msg_data [MAX_MSG__DATA]; /* данные сообщения */
};

    Тип используется при выборе сообщения из очереди. Следует отметить, что операционная система накладывает ограничения на максимальный размер сообщений, количество сообщений в одной очереди и общее количество сообщений, записанное во все очереди. Соответствующие ограничения задаются константами MSGMAX, MSGNNB, MSGTQL в файле <sys/msg.h>.

    Записать сообщение в очередь можно, используя функцию msgsnd( ):

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd (int msgid, const void *ptr, size_t nbytes, int flag;

    Здесь :

msgid - идентификатор очереди, полученный при вызове msgget( );
ptr - указатель на сообщение, записываемое в очередь;
nbytes - размер сообщения в байтах;
flag - либо 0, либо IPC_NOWAIT; в первом случае, если очередь полна, то msgsnd( ) ждет ее освобождения; во втором случае функция при полной очереди возвращается сразу с кодом -1.

     Получение сообщений из очереди производится вызовом функции msgrcv( ). Формат вызова функции следующий:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgrcv (int msgid, void *ptr, size_t nbytes, long type, int flag);

    Здесь:

msgid - идентификатор очереди, полученный при вызове msgget( );
ptr - указатель на буфер для приема сообщения;
nbytes - размер буфера;
type - тип выбираемого из очереди сообщения; если значение этого параметра равно 0, из очереди выбирается первое по порядку сообщение; если type больше 0, из очереди выбирается первое сообщение, поле msg_type которого (см. определение msg_struct) содержит значение равное значению, хранящемуся в первых четырех байтах буфера приема сообщения; если же type меньше 0, будет выбрано сообщение, имеющее минимальное значение поля msg_type;
flag - либо 0, либо IPC_NOWAIT; в первом случае, если очередь пуста, то msgrcv() ждет прихода события; во втором случае функция при пустой очереди возвращается сразу с кодом -1.

     Для управления состоянием очереди сообщений используется системный вызов msgctl( ). Функция позволяет получить информацию о состоянии очереди, изменить права доступа процессов к ней или удалить объект. Ее прототип следующий:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl (int msgid, int cmd, struct msqid_ms *buf);

Здесь:

msgid - идентификатор очереди, полученный при вызове msgget( );
cmd - команда управления очередью; значения параметра определяются константами: IPC_STAT - получить состояние очереди, IPC_SET - установить параметры очереди, и IPC_RMID - удалить очередь сообщений;
buf - адрес структуры данных, используемой при выполнении команд, задаваемых параметром cmd.

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

Оба процесса используют файл заголовок "message.h".

#include <sys/types.h>
#include <sys/ips.h>
#include <sys/msg.h>

#define MSQ_ID   2001     /* уникальный ключ очереди */
#define PERMS    00666    /* права доступа - все могут
                             читать и писать */
#define MSG_TYPE_STRING 1 /* тип сообщения о том, что
                             передана непустая строка */
#define MSG_TYPE_FINISH 2 /* тип сообщения о том, что
                             пора завершать обмен */
                          
#define MAX_STRING 120    /* максимальная длина строки */
          
typedef struct            /* структура сообщения */
{
  int type;
  char string [MAX_STRING];
}
message_t;

    Код программы-клиента:

#include <stdio.h>
#include <string.h>
#include "message.h"

void sys_err (char * msg)
{
  puts (msg);
  exit (1);
}

int main ()
{
  int msqid;      /* идентификатор очереди сообщений */
  message_t msg;  /* сообщение */
  char s [MAX_STRING];
  
  /* создание очереди */
  if ( (msqid = msgget (MSQ_ID, 0)) < 0)
    sys_err ("client:can not get msg queue");

  while (1) {
    scanf ("%s", s); /* ввод строки */
    if (strlen (s) != 1) {
      msg.type = MSG_TYPE_STRING;
      strncpy (msg.string, s, MAX_STRING);
    }
    else
    {
      msg.type = MSG_TYPE_FINISH;
    };
    /* посылка сообщения процессу-серверу */
    if (msgsnd (msqid, &msg, sizeof (message_t), 0) != 0)
      sys_err ("client: message send error");
    if (strlen (s) == 1) /* пустая строка - выход */
        break;
  }
      exit (0);
}

    Код программы-сервера:

#include <stdio.h>
#include <string.h>
#include "message.h"

void sys_err (char * msg)
{
  puts (msg);
  exit (1);
};

int main ()
{
  int msqid;
  message_t msg;
  char s [MAX_STRING];

  /* создание очереди сообщений */
  if ( (msqid = msgget (MSQ_ID, PERMS | IPC_CREAT) ) < 0)
    sys_err ("server: can not create msg queue");

  while (1) {
    /* получение очередного сообщения */
    if (msgrcv (msqid, &msg, sizeof (message_t), 0, 0) < 0)
      sys_err ("server: msg recive error");
      
    if (msg.type == MSG_TYPE_STRING) /* печать строки */
      printf ("%s", msg.string);

    if (msg.type == MSG_TYPE_FINISH) /* выход из цикла */
      break;
  }

  /* удаление очереди сообщений */ 
  if (msgctl (msqid, IPC_RMID, (struct msqid_ds *) 0) < 0)
    sys_err ("server: msq queue remove error");

  exit (0);
}