next up previous contents
Next: Семафоры. Up: Очереди сообщений. Previous: Управление очередями сообщений   Contents

Операции над очередями сообщений

В справочной статье msgop синтаксис упомянутых системных вызовов описан так:

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

int msgsnd (int msqid, struct msgbuf * msgp, int msgsz, int msgflg)

int msgrcv (int msqid, struct msgbuf * msgp, int msgsz, long msgtyp,

    int msgflg)

При успешном завершении системного вызова msgsnd() результат равен нулю; в случае неудачи возвращается -1. В качестве аргумента msqid должен выступать идентификатор очереди сообщений, предварительно полученный при помощи системного вызова msgget. Аргумент msgp является указателем на структуру в области памяти пользователя, содержащую тип посылаемого сообщения и его текст. Аргумент msgsz специфицирует длину массива символов в структуре данных, указываемой аргументом msgp, то есть длину сообщения. Максимально допустимый размер данного массива определяется системным параметром MSGMAX.

Отметим, что значение поля msg_qbytes у ассоциированной структуры данных может быть уменьшено с предполагаемой по умолчанию величины MSGMNB при помощи управляющего действия IPC_SET системного вызова msgctl, однако впоследствии увеличить его может только суперпользователь. Аргумент msgflg позволяет специфицировать выполнение над сообщением ``операции с блокировкой''; для этого флаг IPC_NOWAIT должен быть сброшен (msgflg & IPC_NOWAIT = 0). Блокировка имеет место, если либо текущее число байт в очереди уже равно максимально допустимому значению для указанной очереди (то есть значению поля msg_qbytes или MSGMNB), либо общее число сообщений во всех очередях равно максимально допустимому системой (системный параметр MSGTQL). Если в такой ситуации флаг IPC_NOWAIT установлен, системный вызов msgsnd() завершается неудачей и возвращает -1.

При успешном завершении системного вызова msgrcv() результат равен числу принятых байт; в случае неудачи возвращается -1. В качестве аргумента msqid должен выступать идентификатор очереди сообщений, предварительно полученный при помощи системного вызова msgget. Аргумент msgp является указателем на структуру в области памяти пользователя, содержащую тип принимаемого сообщения и его текст. Аргумент msgsz специфицирует длину принимаемого сообщения. Можно указать, что в случае, если значение данного аргумента меньше, чем длина сообщения в массиве, должна возникать ошибка (см. описание аргумента msgflg).

Аргумент msgtyp используется для выбора из очереди первого сообщения определенного типа. Если значение аргумента равно нулю, запрашивается первое сообщение в очереди, если больше нуля - первое сообщение типа msgtyp, а если меньше нуля - первое сообщение наименьшего из типов, которые не превосходят абсолютной величины аргумента msgtyp.

Аргумент msgflg позволяет специфицировать выполнение над сообщением ``операции с блокировкой''; для этого должен быть сброшен флаг IPC_NOWAIT (msgflg & IPC_NOWAIT = 0). Блокировка имеет место, если в очереди сообщений нет сообщения с запрашиваемым типом (msgtyp). Если флаг IPC_NOWAIT установлен и в очереди нет сообщения требуемого типа, системный вызов немедленно завершается неудачей. Аргумент msgflg может также специфицировать, что системный вызов должен заканчиваться неудачей, если размер сообщения в очереди больше значения msgsz; для этого в данном аргументе должен быть сброшен флаг MSG_NOERROR (msgflg & MSG_NOERROR = 0). Если флаг MSG_NOERROR установлен, сообщение обрезается до длины, указанной аргументом msgsz.

В приведенном ниже примере (рис. 19) используются следующие переменные:

нужен ли флаг IPC_NOWAIT.

комбинацию флагов IPC_NOWAIT и MSG_NOERROR.

Структура данных msqid_ds снабжается указателем на нее, который инициализируется соответствующим образом; это позволяет следить за полями ассоциированной структуры данных, которые могут измениться в результате операций над сообщениями. При помощи системного вызова msgctl() (действие IPC_STAT) программа получает значения полей ассоциированной структуры данных и выводит их. Прежде всего программа запрашивает, какую операцию нужно выполнить - послать или принять сообщение. Должно быть введено число, соответствующее требуемой операции; это число заносится в переменную choice.

Если выбрана операция посылки сообщения, указатель msgp инициализируется адресом структуры данных sndbuf. После этого запрашивается идентификатор очереди сообщений, в которую должно быть послано сообщение; идентификатор заносится в переменную msqid. Затем должен быть введен тип сообщения; он заносится в поле mtype структуры данных, указываемой значением msgp.

После этого программа приглашает ввести с клавиатуры текст посылаемого сообщения и выполняет цикл, в котором символы читаются и заносятся в массив mtext структуры данных. Ввод продолжается до тех пор, пока не будет обнаружен признак конца файла; для функции getchar() таким признаком является символ CTRL+D, непосредственно следующий за символом возврата каретки. После того как признак конца обнаружен, определяется размер сообщения - он на единицу больше значения счетчика i, поскольку элементы массива, в который заносится сообщение, нумеруются с нуля. Следует помнить, что сообщение будет содержать заключительные символы и, следовательно, будет казаться, что сообщение на три символа короче, чем указывает аргумент msgsz.

Чтобы обеспечить пользователю обратную связь, текст сообщения, содержащийся в массиве mtext структуры sndbuf, немедленно выводится на экран.

Следующее, и последнее, действие заключается в определении, должен ли быть установлен флаг IPC_NOWAIT. Чтобы выяснить это, программа предлагает ввести 1, если флаг нужно установить, или любое другое число, если он не нужен. Введенное значение заносится в переменную flag. Если введена единица, аргумент msgflg полагается равным IPC_NOWAIT, в противном случае msgflg устанавливается равным нулю.

После этого выполняется системный вызов msgsnd(). Если вызов завершается неудачей, выводится сообщение об ошибке, а также ее код. Если вызов завершается успешно, печатается возвращенное им значение, которое должно быть равно нулю.

При каждой успешной посылке сообщения обновляются три поля ассоциированной структуры данных. Изменения можно описать следующим образом:

После каждой успешной операции посылки сообщения значения этих полей выводятся на экран.

Если указано, что требуется принять сообщение, начальное значение указателя msgp устанавливается равным адресу структуры данных rcvbuf. Запрашивается код требуемой комбинации флагов, который заносится в переменную flags. Переменная msgflg устанавливается в сответствии с выбранной комбинацией. В заключение запрашивается, сколько байт нужно принять; указанное значение заносится в переменную msgsz. После этого выполняется системный вызов msgrcv().

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

msg_qnum - Определяет общее число сообщений в очереди; в результате выполнения операции уменьшается на единицу.

msg_lrpid - Содержит идентификатор процесса, который последним получил сообщение; полю присваивается соответствующий идентификатор.

msg_rtime - Содержит время последнего получения сообщения, время измеряется в секундах, начиная с 00:00:00 1 января 1970 года (по Гринвичу).

/* Программа иллюстрирует

возможности системных вызовов msgsnd() и msgrcv()

(операции над очередями сообщений) */

#include <stdio.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#define MAXTEXTSIZE 8192

 

struct msgbufl {

  long mtype;

  char mtext [MAXTEXTSIZE];

  } sndbuf, rcvbuf, *msgp;

 

main ()

{

  extern int errno;

  int flag, flags, choice, rtrn, i, c;

  int rtrn, msqid, msgsz, msgflg;

  long msgtyp;

  struct msqid_ds msqid_ds, *buf;

  buf = &msqid_ds;

 

  /* Выбрать требуемую операцию */

  printf ("\nВведите код, соответствующий ");

  printf ("посылке или приему сообщения:\n");

  printf ("  Послать = 1\n");

  printf ("  Принять = 2\n");

  printf ("  Выбор   = ");

  scanf ("%d", &choice);

  if (choice == 1) {

 

     /* Послать сообщение */

     msgp = &sndbuf; /* Указатель на структуру */

     printf ("\nВведите идентификатор ");

     printf ("очереди сообщений,\n");

     printf ("в которую посылается сообщение: ");

     scanf ("%d", &msqid);

  

     /* Установить тип сообщения */

     printf ("\nВведите положительное число - ");

     printf ("тип сообщения: ");

     scanf ("%d", &msgp->mtype);

   

     /* Ввести посылаемое сообщение */

     printf ("\nВведите сообщение: \n");

     /* Управляющая последовательность CTRL+D

     завершает ввод сообщения */

 

     /* Прочитать символы сообщения

     и поместить их в массив mtext */

     for (i = 0; ((c = getchar ()) != EOF); i++)

        sndbuf.mtext [i] = c;

     /* Определить размер сообщения */

     msgsz = i + 1;

     /* Выдать текст посылаемого сообщения */

     for (i = 0; i < msgsz; i++)

        putchar (sndbuf.mtext [i]);

   

     /* Установить флаг IPC_NOWAIT, если это нужно */

     printf ("\nВведите 1, если хотите установить ");

     printf ("флаг IPC_NOWAIT: ");

     scanf ("%d", &flag);

     if (flag == 1) msgflg = IPC_NOWAIT;

     else msgflg = 0;

 

     /* Проверить флаг */

     printf ("\nФлаг = 0%o\n", msgflg);

 

     /* Послать сообщение */

     rtrn = msgsnd (msqid, msgp, msgsz, msgflg);

     if (rtrn == -1) {

        printf ("\nmsgsnd завершился неудачей!\n");

        printf ("Код ошибки = %d\n", errno);

       }

     else {

        /* Вывести результат; при успешном

        завершении он должен равняться нулю */

        printf ("\nРезультат = %d\n", rtrn);

   

        /* Вывести размер сообщения */

        printf ("\nРазмер сообщения = %d\n", msgsz);

        /* Опрос измененной структуры данных */

        msgctl (msqid, IPC_STAT, buf);

        /* Вывести изменившиеся поля */

        printf ("Число сообщений в очереди = %d\n",

                buf->msg_qnum);

        printf ("Ид-р последнего отправителя = %d\n",

                buf->msg_lspid);

        printf ("Время последнего отправления = %d\n",

                buf->msg_stime);

       }

     }

     if (choice == 2) {

       /* Принять сообщение */

       msgp = &rcvbuf;

       /* Определить нужную очередь сообщений */

       printf ("\nВведите ид-р очереди сообщений: ");

       scanf ("%d", &msqid);

       /* Определить тип сообщения */

       printf ("\nВведите тип сообщения: ");

       scanf ("%d", &msgtyp);

       /* Сформировать управляющие флаги

         для требуемых действий */

       printf ("\nВведите код, соответствущий ");

       printf ("нужной комбинации флагов:\n");

       printf ("  Нет флагов               = 0\n");

       printf ("  MSG_NOERROR              = 1\n");

       printf ("  IPC_NOWAIT               = 2\n");

       printf ("  MSG_NOERROR и IPC_NOWAIT = 3\n");

       printf ("  Выбор                    = ");

       scanf ("%d", &flags);

       switch (flags) {

          /* Установить msgflg как побитное ИЛИ

           соответствующих констант */

           case 0:

                  msgflg = 0;

                  break;

           case 1:

                  msgflg = MSG_NOERROR;

                  break;

           case 2:

                  msgflg = IPC_NOWAIT;

                  break;

           case 3:

                  msgflg = MSG_NOERROR | IPC_NOWAIT;

                  break;

          }

      /* Определить, какое число байт принять */

      printf ("\nВведите число байт, которое ");

      printf ("нужно принять (msgsz): ");

      scanf ("%d", &msgsz);

      /* Проверить значение аргументов */

      printf ("\nИдентификатор msqid = %d\n", msqid);

      printf ("Тип сообщения = %d\n", msgtyp);

      printf ("Число байт = %d\n", msgsz);

      printf ("Флаги = %o\n", msgflg);

      /* Вызвать msgrcv для приема сообщения */

      rtrn = msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);

      if (rtrn == -1) {

          printf ("\nmsgrcv завершился неудачей!\n");

          printf ("Код oшибки = %d\n", errno);

         }

      else {

          printf ("\nmsgrcv завершился успешно,\n");

          printf ("идентификатор очереди = %d\n", msqid);

          /* Напечатать число принятых байт,

          оно равно возвращаемому значению */

          printf ("Принято байт: %d\n", rtrn);

          /* Распечатать принятое сообщение */

          for (i = 0; i < rtrn; i++) putchar (rcvbuf.mtext [i]);

         }

       /* Опрос ассоциированной структуры данных */

       msgctl (msqid, IPC_STAT, buf);

       printf ("\nЧисло сообщений в очереди = %d\n",

               buf->msg_qnum);

       printf ("Ид-р последнего получателя = %d\n",

               buf->msg_lrpid);

       printf ("Время последнего получения = %d\n",

               buf->msg_rtime);

       }

       exit (0);

 }

Рис. 19. Пример обмена через очередь сообщений.


next up previous contents
Next: Семафоры. Up: Очереди сообщений. Previous: Управление очередями сообщений   Contents
2003-12-09