next up previous contents
下一个: 系统调用:msgctl() 上一级: 6.4.2 消息队列 上一个: 系统调用:msgget()

系统调用:msgsnd()

一旦我们有了队列标识符,我们就可以开始对其执行操作。 要将消息传递到队列,您可以使用msgsnd系统调用


  SYSTEM CALL: msgsnd();                                                          

  PROTOTYPE: int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg );
    RETURNS: 0 on success
             -1 on error: errno = EAGAIN (queue is full, and IPC_NOWAIT was asserted)
                                  EACCES (permission denied, no write permission)
                                  EFAULT (msgp address isn't accessable - invalid)
                                  EIDRM  (The message queue has been removed)
                                  EINTR  (Received a signal while waiting to write)
                                  EINVAL (Invalid message queue identifier, nonpositive
                                          message type, or invalid message size) 
                                  ENOMEM (Not enough memory to copy message buffer)
  NOTES:

的第一个参数msgsnd是我们的队列标识符,由之前对msgget的调用返回。 第二个参数,msgp,是指向我们重新声明和加载的消息缓冲区的指针。msgsz参数包含消息的大小(以字节为单位),不包括消息类型的长度(4 字节 long)。

msgflg参数可以设置为 0(忽略),或者

IPC_NOWAIT

如果消息队列已满,则消息不会写入队列,控制权返回给调用进程。 如果未指定,则调用进程将挂起(阻塞),直到消息可以写入为止。

让我们创建另一个用于发送消息的包装函数


int send_message( int qid, struct mymsgbuf *qbuf )
{
        int     result, length;

        /* The length is essentially the size of the structure minus sizeof(mtype) */
        length = sizeof(struct mymsgbuf) - sizeof(long);        

        if((result = msgsnd( qid, qbuf, length, 0)) == -1)
        {
                return(-1);
        }
        
        return(result);
}

这个小函数尝试将驻留在传递的地址(qbuf)的消息发送到由传递的队列标识符(qid)指定的消息队列。 这是使用我们到目前为止开发的两个包装函数的示例代码片段


#include <stdio.h>
#include <stdlib.h>
#include <linux/ipc.h>
#include <linux/msg.h>

main()
{
        int    qid;
        key_t  msgkey;
        struct mymsgbuf {
                long    mtype;          /* Message type */
                int     request;        /* Work request number */
                double  salary;         /* Employee's salary */
        } msg;

        /* Generate our IPC key value */
        msgkey = ftok(".", 'm');

        /* Open/create the queue */
        if(( qid = open_queue( msgkey)) == -1) {
                perror("open_queue");
                exit(1);
        }

        /* Load up the message with arbitrary test data */
        msg.mtype   = 1;        /* Message type must be a positive number! */   
        msg.request = 1;        /* Data element #1 */
        msg.salary  = 1000.00;  /* Data element #2 (my yearly salary!) */
        
        /* Bombs away! */
        if((send_message( qid, &msg )) == -1) {
                perror("send_message");
                exit(1);
        }
}

在创建/打开我们的消息队列之后,我们继续用测试数据加载消息缓冲区(注意缺少字符数据以说明我们发送二进制信息的重点)。 快速调用send_message愉快地将我们的消息分发到消息队列。

现在我们的队列中有一条消息,请尝试ipcs命令来查看队列的状态。 现在让我们将讨论转向从队列中实际检索消息。 为此,您可以使用msgrcv()系统调用


  SYSTEM CALL: msgrcv();                                                          
  PROTOTYPE: int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg );
    RETURNS: Number of bytes copied into message buffer
             -1 on error: errno = E2BIG  (Message length is greater than msgsz, no MSG_NOERROR)
                                  EACCES (No read permission)
                                  EFAULT (Address pointed to by msgp is invalid)
                                  EIDRM  (Queue was removed during retrieval)
                                  EINTR  (Interrupted by arriving signal)
                                  EINVAL (msgqid invalid, or msgsz less than 0)
                                  ENOMSG (IPC_NOWAIT asserted, and no message exists
                                          in the queue to satisfy the request) 
  NOTES:

显然,第一个参数用于指定消息检索过程中要使用的队列(应该由之前对msgget的调用返回)。 第二个参数(msgp)表示用于存储检索到的消息的消息缓冲区变量的地址。 第三个参数(msgsz)表示消息缓冲区结构的大小,不包括mtype成员的长度。 同样,这可以很容易地计算为


msgsz = sizeof(struct mymsgbuf) - sizeof(long);

第四个参数(mtype)指定要从队列中检索的消息类型。 内核将搜索队列中具有匹配类型的最旧消息,并将其副本返回到由msgp参数指向的地址。 存在一种特殊情况。 如果以零值传递mtype参数,则返回队列中最旧的消息,而不管其类型如何。

如果将 IPC_NOWAIT 作为标志传递,并且没有消息可用,则该调用将向调用进程返回 ENOMSG。 否则,调用进程将阻塞,直到队列中收到满足msgrcv()参数的消息。 如果在客户端等待消息时删除队列,则返回 EIDRM。 如果进程在阻塞并等待消息到达的过程中捕获到信号,则返回 EINTR。

让我们检查一个用于从队列中检索消息的快速包装函数


int read_message( int qid, long type, struct mymsgbuf *qbuf )
{
        int     result, length;

        /* The length is essentially the size of the structure minus sizeof(mtype) */
        length = sizeof(struct mymsgbuf) - sizeof(long);        

        if((result = msgrcv( qid, qbuf, length, type,  0)) == -1)
        {
                return(-1);
        }
        
        return(result);
}

成功从队列中检索消息后,队列中的消息条目将被销毁。

msgflg参数中的 MSG_NOERROR 位提供了一些额外的功能。 如果物理消息数据的大小大于msgsz,并且断言了 MSG_NOERROR,则将截断消息,并且仅返回msgsz个字节。 通常,msgrcv()系统调用返回 -1 (E2BIG),并且该消息将保留在队列中以供以后检索。 此行为可用于创建另一个包装函数,该函数将允许我们``窥视''队列内部,以查看是否已收到满足我们请求的消息


int peek_message( int qid, long type )
{
        int     result, length;

        if((result = msgrcv( qid, NULL, 0, type,  IPC_NOWAIT)) == -1)
        {
                if(errno == E2BIG)
                        return(TRUE);
        }
        
        return(FALSE);
}

在上面,您会注意到缺少缓冲区地址和长度。 在这种特殊情况下,我们希望调用失败。 但是,我们检查 E2BIG 的返回,这表明确实存在与我们请求的类型匹配的消息。 包装函数在成功时返回 TRUE,否则返回 FALSE。 另请注意 IPC_NOWAIT 标志的使用,该标志可防止先前描述的阻塞行为。


next up previous contents
下一个: 系统调用:msgctl() 上一级: 6.4.2 消息队列 上一个: 系统调用:msgget()

转换于
美国东部标准时间 1996 年 3 月 29 日星期五 14:43:04