BUGS

在Linux 3.13和更早版本中,如果使用MSG_COPY标志而不是IPC_NOWAIT调用msgrcv(),并且消息队列包含的消息少于msgtyp消息,则该调用将阻塞,直到下一条消息写入队列为止。此时,无论该消息是否位于msgtyp的顺序位置,该调用都将返回该消息的副本。此错误已在Linux 3.14中修复。

在msgflg中同时指定MSG_COPY和MSC_EXCEPT是一个逻辑错误(因为这些标志对msgtyp施加了不同的解释)。在Linux 3.13和更早版本中,msgrcv()并未诊断出此错误。此错误已在Linux 3.14中修复。

备注

在Linux或任何版本的POSIX上,都不需要包含和。但是,一些旧的实现需要包含这些头文件,并且SVID也记录了这些头文件。打算移植到这样的旧系统的应用程序可能需要包含这些头文件。

msgp参数在glibc 2.0和2.1中声明为struct msgbuf *。根据SUSv2和SUSv3的要求,在glibc 2.2及更高版本中将其声明为void *。

消息队列资源的以下限制会影响msgsnd()调用:

MSGMAX
消息文本的最大大小,以字节为单位(默认值:8192字节)。在Linux上,可以通过/ proc / sys / kernel / msgmax读取和修改此限制。
MSGMNB
消息队列中可以保留的最大字节数(默认值:16384字节)。在Linux上,可以通过/ proc / sys / kernel / msgmnb读取和修改此限制。特权进程(Linux:具有CAP_SYS_RESOURCE功能的进程)可以使用msgctl(2)IPC_SET操作将消息队列的大小增加到MSGMNB之外。

该实现对消息标头(MSGTQL)的数量和消息池(MSGPOOL)中的字节数没有系统范围的内在限制。

说明

msgsnd()和msgrcv()系统调用用于向System V消息队列发送消息和从中接收消息。调用过程必须对消息队列具有写权限才能发送消息,并具有读权限才能接收消息。

msgp参数是指向以下常规形式的调用方定义的结构的指针:

struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[1];    /* message data */
};

mtext字段是一个数组(或其他结构),其大小由msgsz(非负整数值)指定。长度为零的消息(即无多行文本字段)是允许的。 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设置为调用进程的进程ID。
*
msg_qnum增加1。
*
msg_stime设置为当前时间。

msgrcv()

msgrcv()系统调用从msqid指定的队列中删除一条消息,并将其放入msgp指向的缓冲区中。

msgsz参数指定msgp参数指向的结构的成员mtext的最大大小(以字节为单位)。如果消息文本的长度大于msgsz,则行为取决于是否在msgflg中指定了MSG_NOERROR。如果指定了MSG_NOERROR,则消息文本将被截断(并且截断的部分将丢失);如果未指定MSG_NOERROR,则不会从队列中删除该消息,并且系统调用无法返回-1,并且errno设置为E2BIG。

除非在msgflg中指定了MSG_COPY(请参见下文),否则msgtyp参数将指定请求的消息类型,如下所示:

*
如果msgtyp为0,则读取队列中的第一条消息。
*
如果msgtyp大于0,则将读取msgtyp类型的队列中的第一条消息,除非在msgflg中指定了MSG_EXCEPT,在这种情况下,将读取队列中不等于msgtyp类型的第一条消息。
*
如果msgtyp小于0,则将读取队列中最小类型小于或等于msgtyp绝对值的第一条消息。

msgflg参数是通过对以下零个或多个标志进行"或"运算而构成的位掩码:

IPC_NOWAIT
如果队列中没有请求类型的消息,则立即返回。系统调用失败,将errno设置为ENOMSG。
MSG_COPY(since Linux 3.8)
在msgtyp指定的队列中的序数位置无损获取消息的副本(消息被视为从0开始编号)。
必须与IPC_NOWAIT一起指定此标志,其结果是,如果在给定位置没有消息可用,则调用立即失败,并显示错误ENOMSG。因为它们以正交方式更改msgtyp的含义,所以可能无法在msgflg中同时指定MSG_COPY和MSG_EXCEPT。
添加了MSG_COPY标志用于实现内核检查点还原功能,并且仅当内核是使用CONFIG_CHECKPOINT_RESTORE选项构建的时才可用。
MSG_EXCEPT
与大于0的msgtyp一起使用,以读取消息类型不同于msgtyp的队列中的第一条消息。
MSG_NOERROR
如果长度超过msgsz字节,则截断消息文本。

如果没有可用的请求类型的消息,并且在msgflg中未指定IPC_NOWAIT,则调用过程将被阻塞,直到出现以下情况之一:

*
所需类型的消息被放入队列中。
*
消息队列已从系统中删除。在这种情况下,系统调用将errno设置为EIDRM而失败。
*
调用过程捕获信号。在这种情况下,系统调用将errno设置为EINTR失败。 (msgrcv()永远不会在被信号处理程序中断后自动重新启动,而不管建立信号处理程序时是否设置了SA_RESTART标志。)

成功完成后,消息队列数据结构将更新如下:

msg_lrpid设置为调用进程的进程ID。
msg_qnum减1。
msg_rtime设置为当前时间。

语法

#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);

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/

示例

下面的程序演示了msgsnd()和msgrcv()的用法。

示例程序首先使用-s选项运行以发送消息,然后再次使用-r选项运行以接收消息。

以下shell会话显示了该程序的示例运行:

$ ./a.out -s
sent: a message at Wed Mar  4 16:25:45 2015

$ ./a.out -r
message received: a message at Wed Mar  4 16:25:45 2015

Program source

#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, "Usage: %s [options]\n", prog_name);
    fprintf(stderr, "Options are:\n");
    fprintf(stderr, "-s        send message using msgsnd()\n");
    fprintf(stderr, "-r        read message using msgrcv()\n");
    fprintf(stderr, "-t        message type (default is 1)\n");
    fprintf(stderr, "-k        message queue key (default is 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), "a message at %s",
            ctime(&t));

    if (msgsnd(qid, (void *) &msg, sizeof(msg.mtext),
                IPC_NOWAIT) == -1) {
        perror("msgsnd error");
        exit(EXIT_FAILURE);
    }
    printf("sent: %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("No message available for msgrcv()\n");
    } else
        printf("message received: %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 aqsaq:
            mode = 1;
            break;
        case aqraq:
            mode = 2;
            break;
        case aqtaq:
            msgtype = atoi(optarg);
            if (msgtype <= 0)
                usage(argv[0], "-t option must be greater than 0\n");
            break;
        case aqkaq:
            msgkey = atoi(optarg);
            break;
        default:
            usage(argv[0], "Unrecognized option\n");
        }
    }

    if (mode == 0)
        usage(argv[0], "must use either -s or -r option\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);
}

返回值

失败时,两个函数均返回-1,并带有errno指示错误,否则msgsnd()返回0,而msgrcv()返回实际复制到mtext数组中的字节数。

另外参见

msgctl(2),msgget(2),功能(7),mq_overview(7),sysvipc(7)

名称

msgrcv,msgsnd-系统V消息队列操作

错误说明

msgsnd()失败时,errno将被设置为以下值之一:

EACCES
调用过程对消息队列没有写权限,并且在控制其IPC名称空间的用户名称空间中不具有CAP_IPC_OWNER功能。
EAGAIN
由于队列的msg_qbytes限制,并且msgflg中指定了IPC_NOWAIT,因此无法发送该消息。
EFAULT
msgp指向的地址不可访问。
EIDRM
消息队列已删除。
EINTR
在消息队列已满的情况下休眠,该进程捕获了一个信号。
EINVAL
无效的msqid值,非正mtype值或无效的msgsz值(小于0或大于系统值MSGMAX)。
ENOMEM
系统没有足够的内存来复制msgp指向的消息。

msgrcv()失败时,errno将被设置为以下值之一:

E2BIG
消息文本长度大于msgsz,并且msgflg中未指定MSG_NOERROR。
EACCES
调用过程对消息队列没有读取权限,并且在控制其IPC名称空间的用户名称空间中不具有CAP_IPC_OWNER功能。
EFAULT
msgp指向的地址不可访问。
EIDRM
当进程处于休眠状态以接收消息时,消息队列被删除。
EINTR
当进程处于睡眠状态以接收消息时,进程捕获了一个信号。参见signal(7)。
EINVAL
msqid无效,或msgsz小于0。
EINVAL(since Linux 3.14)
msgflg指定了MSG_COPY,但未指定IPC_NOWAIT。
EINVAL(since Linux 3.14)
msgflg指定了MSG_COPY和MSG_EXCEPT。
ENOMSG
在msgflg中指定了IPC_NOWAIT,并且消息队列中不存在所请求类型的消息。
ENOMSG
在msgflg中指定了IPC_NOWAIT和MSG_COPY,并且该队列包含的消息少于msgtyp消息。
ENOSYS(since Linux 3.8)
在msgflg中指定了MSG_COPY,并且配置了该内核时未配置CONFIG_CHECKPOINT_RESTORE。

遵循规范

POSIX.1-2001,POSIX.1-2008,SVr4。

MSG_EXCEPT和MSG_COPY标志是特定于Linux的。它们的定义可以通过定义_GNU_SOURCE功能测试宏来获得。

MSGOP - Linux手册页

Linux程序员手册 第2部分
更新日期: 2020-04-11

日期:2019-08-20 17:59:01 来源:oir作者:oir