APUE_消息队列

1.引言   

       消息队列是消息的链接表,存放在内核中并由消息队列标识符标识。

2.创建消息队列

       每一个队列都有一个msqid_ds结构与其相关联。此结构规定了消息队列的当前状态。主要包括XSI IPC共同的信息ipc_perm,队列长度,队列中消息数,和最后对队列进行操作的进程及时间。

struct msqid_ds {
     struct ipc_perm  msg_perm;     
     msgqnum_t     msg_qnum;    /* # of messages on queue */
     msglen_t       msg_qbytes;   /* max # of bytes on queue */
     pid_t          msg_lspid;    /* pid of last msgsnd() */
     pid_t          msg_lrpid;    /* pid of last msgrcv() */
     time_t         msg_stime;    /* last-msgsnd() time */
     time_t         msg_rtime;    /* last-msgrcv() time */
     time_t         msg_ctime;    /* last-change time */
     .
     .
     .
};

      使用消息队列第一个要用到的函数就是msggetmsget函数用于创建一个新队列或打开一个现存的队列。

#include <sys/msg.h>
int msgget(key_t key, int flag);
		//返回值:成功则返回消息队列ID,出错返回-1


      对消息队列的操作需要使用,消息队列ID

3.操作消息队列

      msgctl函数对消息队列执行多种操作。

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf );
		//成功为0,出错为-1


       cmd参数说明对由msgid指定的消息队列要执行的命令。

       IPC_STAT,取此消息队列的msqid_ds结构,并将其放到buf指向的结构中。

       IPC_SET,按由buf指向结构中的值,设置与此队列相关结构中的四个字段:msg_perm.uidmsg_perm.gidmsg_perm.modemsg_qbytes。调用进程的有效用户ID必须等于msg_perm.cuidmsd_perm.uid,或者是具有超级用户权限的进程。只有超级用户才能增加msg_qbytes的值。

       IPC_RMID,从系统中删除该消息队列以及仍在队列中的所有数据。(XSI IPC都是没有引用计数的!!!)这种删除立即生效。调用进程的有效用户ID必须等于msg_perm.cuidmsd_perm.uid,或者是具有超级用户权限的进程。

       这三条命令(IPC_STATIPC_SETIPC_RMID)也可用于信号量或者共享存储。

 

4.发送消息

       msgsnd函数可将数据发送到消息队列尾端。

#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
		//成功为0,出错为-1


       每一个消息由三部分组成:正长整型的类型字段mtype、非负长度nbytes以及实际数据字节(对应于长度)。参数ptr是一个结构的指针,它包含了消息类型mtype和消息数据。这样接受者可以使用消息类型以非先进先出的次序取消息。

struct mymesg {
     long  mtype;      /* positive message type */
     char  mtext[nbytes]; /* message data, of length nbytes */
};


msgsnd成功返回,与消息队列关联的msqid_ds结构得到更新,以标明发出该调用的进程IDmsg_lspid)、进行该调用的时间(msg_stime),并指示队列中增加了一条消息(msg_qnum)。

5.接收消息

       msgrcv函数可从消息队列中取得消息。

#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
		//若成功则返回消息的数据部分的长度,出错返回-1


参数ptrmsgsnd中的一样。nbytes说明数据缓冲区的长度。若返回的消息大于nbytes,而且在flag中设置了MSG_NOERROR,则该消息被截断。在这种情况下,消息的截断部分被丢弃。如果没有设置这一标志,而消息又太长,则出错返回E2BIG,消息仍然留在队列中。

参数type指定想要接收哪一种消息:1type==0,返回队列中的第一个消息;2type>0,返回队列中消息类型为type的第一个消息;3type<0,返回队列中消息类型值小于或等于type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。这就可以实现以非先进先出的方式操作消息队列!!!

msgrcv成功执行时,内核更新与消息队列关联的msqid_ds结构,以指示调用者的IDmsg_lrpid)和调用时间(msg_rtime),并将队列中的消息数(msg_qnum)减1

6.消息队列的劣势

       消息队列原来的实施目的就是提供比一般IPC更高速度的进程通信方式,不过现在与其他IPC相比在速度上没有什么区别了,甚至STREAM管道要比消息队列更加快。而且消息队列具有XSI IPC共有的问题,所以一般不采用消息队列作为进程间通信的手段。

7.示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <time.h>
#include "apue.h"

struct mymesg
{
     long mtype;
     char mtext[6];

};

void print_msqid(const struct msqid_ds *ds)
{
     printf("uid=%d, gid=%d, cuid=%d, cgid=%d, mode=%o/n", 
         ds->msg_perm.uid, ds->msg_perm.gid, ds->msg_perm.cuid, 
         ds->msg_perm.cgid, ds->msg_perm.mode);
     printf("msg_qnum=%d, msg_qbytes=%d/n", 
         (int)ds->msg_qnum, (int)ds->msg_qbytes);
     printf("msg_lspid=%d, msg_lrpid=%d/n", 
         ds->msg_lspid, ds->msg_lrpid);
     printf("msg_stime=%smsg_rtime=%smsg_ctime=%s", 
         ctime(&ds->msg_stime), ctime(&ds->msg_rtime), ctime(&ds->msg_ctime));
}

int main()
{
     pid_t pid;
     key_t key;
     int msgid;
     struct mymesg snd_msg={1, "hello"};
     struct mymesg rcv_msg;
     struct msqid_ds msqds;
     if((key = ftok("temp", 'b')) == -1)
       err_sys("ftok error");
     if((pid = fork()) < 0)
       err_sys("fork error");
     else if(pid == 0)      /*parent*/
     {
       if((msgid = msgget(key, IPC_CREAT | 00666)) == -1)
            err_sys("create msgqueue error");
       if(msgsnd(msgid, &snd_msg, 6, 0) == -1)
            err_sys("msgsnd error");
       exit(0);
     }
     if(waitpid(pid, NULL, 0) == -1)
       err_sys("waitpid error");
     if((msgid = msgget(key, 0)) == -1)
       err_sys("open msgqueue error");
     if(msgrcv(msgid, &rcv_msg, 6, 1, 0) == -1)
       err_sys("msgrcv error");
     fputs(rcv_msg.mtext, stdout);
     fputc('/n', stdout);
     if(msgctl(msgid, IPC_STAT, &msqds) == -1)
       err_sys("msgctl IPC_STAT error");
     print_msqid(&msqds);
     if(msgctl(msgid, IPC_RMID, NULL) == -1)
       err_sys("msgctl IPC_RMID error");
     exit(0);
}


 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值