MSGOP(2)

ИМЯ
msgrcv, msgsnd - операции с очередью сообщений System V

ОБЗОР
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
               int msgflg);

ОПИСАНИЕ
Системные вызовы msgsnd() и msgrcv() используются для отправки и получения сообщений из очереди сообщений System V. Вызывающий процесс должен иметь права на запись в очередь сообщений, чтобы отправлять сообщения и права на чтение для получения сообщений.

Аргумент msgp представляет собой указатель на структуру, определяемую вызывающим как:

    struct msgbuf {
        long mtype;       /* тип сообщения, значение должно быть > 0 */
        char mtext[1];    /* данные сообщения */
    };

Поле mtext является массивом (или другой структурой), размер которого определяется msgsz — неотрицательным целым значением. Разрешены сообщения нулевой длины (т.е. без поля mtext). Поле mtype должно быть только положительным целым значением. Это значение используется процессом-получателем для выбора сообщения (см. описание msgrcv() далее).

  msgsnd()
Системный вызов msgsnd() добавляет копию сообщения, указанного msgp, в очередь сообщений, идентификатор которой задаётся в msqid.

Если в очереди достаточно места, то msgsnd() сразу успешно завершается. Размер очереди управляется полем msg_qbytes в связанной структуре данных очереди сообщений. При создании очереди это поле инициализируется значением MSGMNB байт, но данное ограничение можно изменить с помощью msgctl(2). Очередь сообщений считается заполненной при одном из следующих условий:

*   Добавление нового сообщения привело бы к превышению общего количества байт очереди над максимальным количеством размером очереди (поле msg_qbytes).

*   Добавление другого сообщения привело бы к превышению общего количества сообщений очереди над максимальным количеством размером очереди (поле msg_qbytes). Эта проверка необходима для предотвращения помещения в очередь бесконечного количества сообщений нулевой длины. Хотя такие сообщения не содержат данных, тем не менее, они потребляют (блокируют) память ядра.

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

Заблокированный вызов msgsnd() может завершиться ошибкой если:

*   очередь удалена (в этом случае системный вызов выдаст ошибку, определив errno в значение EIDRM;

*   пойман сигнал; в этом случае системный вызов завершается с ошибкой и присваивает errno значение EINTR; см. signal(7). Вызов msgsnd() никогда не перезапускается автоматически после прерывания обработчиком сигнала, независимо от установки флага SA_RESTART при настройке обработчика сигнала.

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

    Значение msg_lspid устанавливается равным идентификатору вызывающего процесса.

    Значение msg_qnum увеличивается на 1.

    Значение msg_stime присваивается значение текущего времени.

  msgrcv()
Системный вызов msgrcv() удаляет сообщение из очереди, указанной в msqid и помещает его в буфер, указанный в msgp.

Параметр msgsz задаёт максимальный размер (в байтах) элемента mtext структуры, находящейся по адресу, указанному в аргументе msgp. Если длина текста сообщения больше чем msgsz, то поведение зависит от наличия флага MSG_NOERROR в msgflg. Если MSG_NOERROR указан, то текст сообщения будет урезан (а урезанная часть потеряна); иначе сообщение не удаляется из очереди, а системный вызов возвращает -1 и присваивает errno значение E2BIG.

Если в msgflg (смотрите ниже ) не указан MSG_COPY, то в аргументе msgtyp указывается тип запрашиваемого сообщения:

*   Если msgtyp равно нулю, то читается первое сообщение в очереди.

*   Если msgtyp больше нуля, то из очереди читается первое сообщение с типом msgtyp (если только в msgflg не указан MSG_EXCEPT. В этом случае из очереди читается первое сообщение, тип которого не равен msgtyp).

*   Если msgtyp меньше нуля, то из очереди читается первое сообщение со значением, меньшим или равным абсолютному значению msgtyp.

Аргумент msgflg представляет собой битовую маску из комбинации нуля или более следующих флагов:

IPC_NOWAIT
    Немедленный возврат, если в очереди нет сообщений необходимого типа. При этом системный вызов возвращает ошибку, присваивая errno значение ENOMSG.

MSG_COPY (начиная с Linux 3.8)
    Забирает копию сообщения без удаления из начальной позиции в очереди, заданной в msgtyp (сообщения нумеруются начиная с 0).

    Этот флаг должен указываться вместе с IPC_NOWAIT; в этом случае, если в заданной позиции сообщение отсутствует, то вызов завершается с ошибкой ENOMSG немедленно. Флаги MSG_COPY и MSG_EXCEPT не могут указываться вместе в msgflg, так как кардинально изменяют смысл msgtyp.

    Флаг MSG_COPY был добавлен для реализации способности ядра для контрольных точек/восстановления и доступен только, если ядро собрано с параметром CONFIG_CHECKPOINT_RESTORE.

MSG_EXCEPT
    Используется, если msgtyp больше 0, для чтения первого сообщения в очереди с типом, отличным от msgtyp.

MSG_NOERROR
    Используется для урезания текста сообщения, если его размер больше msgsz байт.

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

*   В очередь не будет помещено сообщение необходимого типа.

*   Очередь сообщений удалена из системы. В этом случае системный вызов возвращает ошибку и присваивает errno значение EIDRM.

*   Вызывающий процесс не получит сигнал, который должен обработать. В этом случае системный вызов возвращает ошибку и присваивает переменной errno значение EINTR. Вызов msgrcv() никогда не перезапускается автоматически после прерывания обработчиком сигнала, независимо от установки флага SA_RESTART при настройке обработчика сигнала.

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

    Значение msg_lrpid устанавливается равным идентификатору вызывающего процесса.

    Значение msg_qnum уменьшается на 1.

    Значение msg_rtime становится равным текущему времени.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При ошибке оба вызова возвращают -1, а переменная errno приобретает соответствующее значение. В противном случае msgsnd() возвращает 0, а msgrcv() возвращает количество байтов, скопированных в массив mtext.

ОШИБКИ
Значения errno, устанавливаемые при возникновении ошибок в msgsnd():

EACCES
    Вызывающий процесс не имеет прав записи в очередь сообщений и не имеет мандата CAP_IPC_OWNER.

EAGAIN
    Сообщение не может быть отправлено, так как размер очереди превысит лимит, равный msg_qbytes, а в параметре msgflg установлен флаг IPC_NOWAIT.

EFAULT
    Память с адресом, указанным msgp, недоступна.

EIDRM
    Очередь сообщений была удалена.

EINTR
    Процесс ждал свободного места в очереди и получил сигнал, который должен обработать.

EINVAL
    Задано неправильное значение msqid, не положительное значение mtype или неправильное значение msgsz (меньше 0, или больше системного лимита, заданного MSGMAX).

ENOMEM
    Недостаточно памяти в системе для копирования сообщения, указанного msgp.

Значения errno, устанавливаемые при возникновении ошибок в msgrcv():

E2BIG
    Длина текста получаемого сообщения больше, чем msgsz, а в поле msgflg не установлен флаг MSG_NOERROR.

EACCES
    Вызывающий процесс не имеет прав чтобы прочитать очереди сообщений и не имеет мандата CAP_IPC_OWNER в пространстве имён пользователя, который управляет его пространством имён IPC.

EFAULT
    Память с адресом, указанным msgp, недоступна.

EIDRM
    Процесс ждал приёма сообщения, и в это время очередь сообщений была удалена.

EINTR
    Во время ожидания приёма сообщения процесс получил сигнал; см. signal(7).

EINVAL
    Неверное значение msqid или msgsz меньше 0.

EINVAL (начиная с Linux 3.14)
    В msgflg указан флаг MSG_COPY, но нет IPC_NOWAIT.

EINVAL (начиная с Linux 3.14)
    В msgflg указаны MSG_COPY и MSG_EXCEPT.

ENOMSG
    В очереди нет сообщения необходимого типа, а в параметре msgflg установлен флаг IPC_NOWAIT.

ENOMSG
    Флаги IPC_NOWAIT и MSG_COPY указаны в msgflg, но в очереди содержится меньше msgtyp сообщений.

ENOSYS (начиная с Linux 3.8)
    Флаг MSG_COPY указан в msgflg, но ядро собрано без параметра CONFIG_CHECKPOINT_RESTORE.

СООТВЕТСТВИЕ СТАНДАРТАМ
POSIX.1-2001, POSIX.1-2008, SVr4.

Флаги MSG_EXCEPT и MSG_COPY есть только в Linux; их определения можно получить указав макрос тестирования свойств _GNU_SOURCE.

ЗАМЕЧАНИЯ
Включение файлов <sys/types.h> и <sys/ipc.h> не требуется в Linux или любых версий POSIX. Однако, некоторые старые реализации требуют включения данных заголовочных файлов, и это также требуется по SVID. В приложениях, которые нужно перенести на такие старые системы, может потребоваться включить данных заголовочные файлы.

Аргумент msgp в glibc 2.0 и 2.1 объявлен как struct msgbuf *. В glibc 2.2 и новее, в соответствии с SUSv2 и SUSv3, он объявлен как void *.

На работу системного вызова msgsnd() влияют следующие системные ограничения на ресурсы очереди сообщений:

MSGMAX
    Максимальный размер текста сообщения: по умолчанию он равен 8192 байтам (в Linux это ограничение можно прочитать и изменить через /proc/sys/kernel/msgmax).

MSGMNB
    Максимальное количество байт, которое может храниться в очереди сообщений (по умолчанию 16384 байт). В Linux это ограничение можно прочитать и изменить через /proc/sys/kernel/msgmnb. Привилегированный процесс (Linux: процесс с мандатом CAP_SYS_RESOURCE) может устанавливать размер очереди сообщений больше чем MSGMNB при помощи вызова msgctl() с операцией IPC_SET.

Реализация не накладывает существенных системных ограничений на максимальное количество заголовков сообщений (MSGTQL) и на количество байт в пуле сообщений (MSGPOOL).

ДЕФЕКТЫ
В Linux 3.13 и старее, если msgrcv() был вызван с флагом MSG_COPY flag, но без IPC_NOWAIT, и очередь сообщений содержала менее msgtyp сообщений, то вызов блокировал выполнения до тех пор, пока следующее сообщение не записывалось в очередь. written to the queue. В этот момент вызов возвращал копию сообщения, независимо от того, что сообщение было в начальной позиции msgtyp. Этот дефект был исправлен в Linux 3.14.

Указание обоих флагов MSG_COPY и MSC_EXCEPT в msgflg является логической ошибкой (так как для флагов по-разному интерпретируется msgtyp). В Linux 3.13 и старее эта ошибка не определялась msgrcv(). Этот дефект был исправлен в Linux 3.14.

ПРИМЕР
Представленная ниже программа показывает использование msgsnd() и msgrcv().

В первый раз программа запускается с параметром -s для отправки сообщения, а в второй — с параметром -r для получения сообщения.

Пример сеанса работы с программой:

    $ ./a.out -s
    отправка: дата сообщения Wed Mar  4 16:25:45 2015
    $ ./a.out -r
    приём: дата сообщения Wed Mar  4 16:25:45 2015

  Исходный код программы
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    struct msgbuf {
        long mtype;
        char mtext[80];
    };
    static void
    usage(char *prog_name, char *msg)
    {
        if (msg != NULL)
            fputs(msg, stderr);
        fprintf(stderr, "Использование: %s [параметры]\n", prog_name);
        fprintf(stderr, "Параметры:\n");
        fprintf(stderr, "-s        отправить сообщение с помощью msgsnd()\n");
        fprintf(stderr, "-r        прочитать сообщение с помощью msgrcv()\n");
        fprintf(stderr, "-t        тип сообщения (по умолчанию 1)\n");
        fprintf(stderr, "-k        ключ очереди сообщения (по умолчанию 1234)\n");
        exit(EXIT_FAILURE);
    }
    static void
    send_msg(int qid, int msgtype)
    {
        struct msgbuf msg;
        time_t t;
        msg.mtype = msgtype;
        time(&t);
        snprintf(msg.mtext, sizeof(msg.mtext), "дата сообщения %s",
                ctime(&t));
        if (msgsnd(qid, (void *) &msg, sizeof(msg.mtext),
                    IPC_NOWAIT) == -1) {
            perror("msgsnd error");
            exit(EXIT_FAILURE);
        }
        printf("отправка: %s\n", msg.mtext);
    }
    static void
    get_msg(int qid, int msgtype)
    {
        struct msgbuf msg;
        if (msgrcv(qid, (void *) &msg, sizeof(msg.mtext), msgtype,
                   MSG_NOERROR | IPC_NOWAIT) == -1) {
            if (errno != ENOMSG) {
                perror("msgrcv");
                exit(EXIT_FAILURE);
            }
            printf("С помощью msgrcv() сообщений не получено\n");
        } else
            printf("приём: %s\n", msg.mtext);
    }
    int
    main(int argc, char *argv[])
    {
        int qid, opt;
        int mode = 0;               /* 1 = send, 2 = receive */
        int msgtype = 1;
        int msgkey = 1234;
        while ((opt = getopt(argc, argv, "srt:k:")) != -1) {
            switch (opt) {
            case 's':
                mode = 1;
                break;
            case 'r':
                mode = 2;
                break;
            case 't':
                msgtype = atoi(optarg);
                if (msgtype <= 0)
                    usage(argv[0], "параметр -t должен быть больше 0\n");
                break;
            case 'k':
                msgkey = atoi(optarg);
                break;
            default:
                usage(argv[0], "Неизвестный параметр\n");
            }
        }
        if (mode == 0)
            usage(argv[0], "нужно указать параметр -s или -r\n");
        qid = msgget(msgkey, IPC_CREAT | 0666);
        if (qid == -1) {
            perror("msgget");
            exit(EXIT_FAILURE);
        }
        if (mode == 2)
            get_msg(qid, msgtype);
        else
            send_msg(qid, msgtype);
        exit(EXIT_SUCCESS);
    }

СМОТРИТЕ ТАКЖЕ
msgctl(2), msgget(2), capabilities(7), mq_overview(7), svipc(7)