《unix高级环境编程》进程间通信——消息队列

  消息队列是消息的链接表,保存在内核,通过消息队列的引用标识符来访问消息,消息队列对每个消息指定了特定的消息类型,接收消息的进程可以请求接收下一条消息,也可以请求接收下一条特定类型的消息。系统内核维护的消息队列的结构如下:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <bits/msq.h>  
  2.   
  3. struct msqid_ds  
  4. {  
  5.   struct ipc_perm msg_perm;     /* IPC对象的属性信息和访问权限 */  
  6.   struct msg *msg_first;        /* 指向消息队列的第一个消息 */  
  7.   struct msg *msg_last;         /* 指向消息队列的最后一个消息 */  
  8.   time_t msg_stime;             /* time of last msgsnd command */  
  9.   time_t msg_rtime;             /* time of last msgrcv command */  
  10.   time_t msg_ctime;             /* time of last change */  
  11.   unsigned long int msg_cbytes; /* 当前消息队列中消息的总字节数 */  
  12.   msgqnum_t msg_qnum;           /* 当前队列中消息的个数 */  
  13.   msglen_t msg_qbytes;          /* 队列允许存放的最大字节数 */  
  14.   pid_t msg_lspid;              /* pid of last msgsnd() 即最后执行msgsnd函数的进程的进程ID */  
  15.   pid_t msg_lrpid;              /* pid of last msgrcv() 即最后执行msgrcv函数的进程的进程ID */  
  16.   
  17. };  

其中 ipc_perm 的结构如下:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct ipc_perm  
  2. {  
  3.     uid_t uid;      /* owner's effective user id */  
  4.     gid_t gid;      /* owner's effective group id */  
  5.     uid_t cuid;     /* creator's effective user id */  
  6.     gid_t cgid;     /* creator's effective user id */  
  7.     mode_t mode;    /* access modes */  
  8. };  

内核维护的消息队列链表结构形式如下图所示:


       消息队列所传递的信息有两部分组成,即消息类型及其所传递数据,一般用一个结构表示,通常消息类型是一个正的长整型数表示,而数据根据需要设定,例如设定一个传送1024字节长度的字符数据的消息结构如下。获取消息队列中的消息时,不一定按照先进先出顺序,也可以按照消息的类型字段进行获取。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct msgbuf{  
  2.     long msgtype;  
  3.     char msgtext[1024];  
  4. };  

消息队列的创建与打开

          要是有消息队列,首先必须要创建一个消息队列,msgget 函数能够实现该功能:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:创建一个新的消息队列或打开一个现有的消息队列; 
  3.  * 返回值:若成功则返回消息队列的ID,若出错则返回-1; 
  4.  * 函数原型: 
  5.  */  
  6. #include <sys/msg.h>  
  7.   
  8. int msgget(key_t key, int flag);  
  9. /* 
  10.  * 说明: 
  11.  * 参数key是消息队列的键; 
  12.  * 参数flag表示调用函数的操作类型,也可用于设置访问权限; 
  13.  */  

当成功创建一个新消息队列时,msqid_ds 结构的成员被初始化为如下值:

  1. msg_perm 结构的 uid 和 cuid 成员被设置成当前进程的有效用户ID,gid 和 cgid 成员被设置成当前进程的有效组ID;
  2. flag中的读写权限位存放在 msg_perm.mode 中;
  3. msg_qnum, msg_lspid, msg_lrpid, msg_stime 和 msg_rtime 被置为0;
  4. msg_ctime 被设置成当前时间;
  5. msg_qbytes被设置成系统限制值;
创建一个消息队列的测试程序:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "apue.h"  
  2. #include <fcntl.h>  
  3. #include <sys/msg.h>  
  4.   
  5.   
  6. #define  PATH_NAME "./Queue"  
  7.   
  8. int main(void)  
  9. {  
  10.     key_t key;  
  11.     int fd;  
  12.   
  13.     if ((fd = open(PATH_NAME, O_CREAT, 0666)) < 0)  
  14.         err_quit("open error");  
  15.     close(fd);  
  16.     //生成键值  
  17.     key = ftok(PATH_NAME, 0);  
  18.     int msgID;  
  19.   
  20.     if ((msgID = msgget(key, IPC_CREAT | 0666)) == -1)  
  21.         err_quit("msgget error");  
  22.     printf("key: %x\n", key);  
  23.     printf("msgID: %d\n", msgID);  
  24.     exit(0);  
  25.   
  26. }  
输出结果:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ./msg   
  2. key: 1309d  
  3. msgID: 0  
创建消息队列之后可以在终端上输入命令查看消息队列情况:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ ipcs -q -i 0  
  2.   
  3. Message Queue msqid=0  
  4. uid=1000    gid=1000    cuid=1000   cgid=1000   mode=0666  
  5. cbytes=0    qbytes=16384    qnum=0  lspid=0 lrpid=0  
  6. send_time=Not set                     
  7. rcv_time=Not set                     
  8. change_time=Mon Nov 17 14:35:15 2014  

消息队列的操作

        向消息队列中发送消息,我们可以调用 msgsnd 函数实现该功能:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:向消息队列中发送消息; 
  3.  * 返回值:若成功则返回0,若出错则返回-1; 
  4.  * 函数原型: 
  5.  */  
  6. #include <sys/msg.h>  
  7.   
  8. int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);  
  9. /* 
  10.  * msqid是消息队列的引用标识符; 
  11.  * ptr是一个void指针,指向要发送的消息; 
  12.  * nbytes表示要发送消息的字节数; 
  13.  * flag用于指定消息队列已满时的处理方法,当消息队列为满时,若设置为IPC_NOWAIT,则立刻出错返回EAGAIN; 
  14.  * 否则发送消息的进程被阻塞,直到消息队列中空间或消息队列被删除或捕捉到信号时,函数返回; 
  15.  */  
从消息队列中接收消息,我们可以调用 msgrcv 函数实现该功能:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:从消息队列中接收消息; 
  3.  * 返回值:若成功则返回消息的数据部分的长度,若出错则返回-1; 
  4.  * 函数原型: 
  5.  */  
  6. #include <sys/msg.h>  
  7.   
  8. ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);  
  9. /* 
  10.  * msqid是消息队列的引用标识符; 
  11.  * ptr是一个void指针,指向要存放消息数据的缓冲区; 
  12.  * nbytes表示要存放消息数据缓冲区的长度; 
  13.  * 
  14.  * 当返回的消息实际长度大于nbytes时,根据flag的设置进行处理:若设置为MSG_NOERROR,则消息被截短,否则出错返回E2BIG; 
  15.  * 若指定的type无效时,若flag设置为IPC_NOWAIT,则立即出错返回,且errno设为ENOMSG,否则接收消息的进程将被阻塞, 
  16.  * 直到type有效或者消息队列被删除或者捕捉到信号; 
  17.  * 
  18.  * type的取值如下: 
  19.  * (1)type=0  接收消息队列中的第一条消息; 
  20.  * (2)type>0  接收消息队列中类型为type的第一条消息; 
  21.  * (3)type<0  接收消息队列中类型值小于或等于type绝对值的所有消息中类型最小的消息中的第一条消息; 
  22.  * type为非0,则用于非先进先出顺序读消息; 
  23.  */  

消息队列的控制

        对消息队列的具体控制操作可以通过函数 msgctl 来实现:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 函数功能:消息队列的控制; 
  3.  * 返回值:若成功则返回0,若出错则返回-1; 
  4.  * 函数原型: 
  5.  */  
  6. #include <sys/msg.h>  
  7. int msgctl(int msqid, int cmd, struct msqid_ds *buf);  

根据 cmd 不同的取值有不同的操作,其中 cmd 参数取值如下:
  1. IPC_STAT:获取消息队列中的 msqid_ds 结构,并把它保存在 buf 指向的缓冲区;
  2. IPC_SET:按参数 buf 指向的结构中的值设置该消息队列对应的 msqid_ds 结构中四个字段—— msg_perm.uid,msg_perm.gid,msg_perm.mode 和 msg_qbytes。此操作只能由以下两种进程执行:一种是其有效用户 ID 等于  msg_perm.cuid 或  msg_perm.uid;另一种是具有超级用户权限的进程。只有超级用户才能增加 msg_qbytes 的值;
  3. IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据,该执行立即生效;若还有进程对次消息队列进行操作,则出错返回 EIDRM。此操作只能由以下两种进程执行:一种是其有效用户 ID 等于  msg_perm.cuid 或  msg_perm.uid;另一种是具有超级用户权限的进程。
测试程序:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include "apue.h"  
  2. #include <sys/msg.h>  
  3. #include <fcntl.h>  
  4.   
  5. #define PATH_NAME "./Queue"  
  6.   
  7. key_t MakeKey(const char *pathname);  
  8.   
  9. int main(void)  
  10. {  
  11.     int msgid;  
  12.     int status;  
  13.     key_t key;  
  14.     key = MakeKey(PATH_NAME);  
  15.   
  16.     char str1[] = "test message: Wellcome.";  
  17.     char str2[] = "test message: goodbye.";  
  18.     struct msgbuf  
  19.     {  
  20.         long msgtype;  
  21.         char msgtext[MAXLINE];  
  22.     }sndmsg, rcvmsg;  
  23.   
  24.     if((msgid = msgget(key, IPC_CREAT | 0666)) == -1)  
  25.         err_quit("msgget error");  
  26.     
  27.         sndmsg.msgtype = 100;  
  28.         sprintf(sndmsg.msgtext, str1);  
  29.         if(msgsnd(msgid, (struct msgbuf *)&sndmsg, sizeof(str1)+1, 0) == -1)  
  30.             err_quit("msgsnd error");  
  31.         sndmsg.msgtype = 200;  
  32.         sprintf(sndmsg.msgtext, str2);  
  33.         if(msgsnd(msgid, (struct msgbuf *)&sndmsg, sizeof(str2)+1, 0) == -1)  
  34.             err_quit("msgsnd error");  
  35.   
  36.   
  37.     if((status = msgrcv(msgid, (struct msgbuf*)&rcvmsg, 128, 100, IPC_NOWAIT)) == -1)  
  38.         err_quit("msgrcv error");  
  39.   
  40.     printf("Recevied message:\n%s\n", rcvmsg.msgtext);  
  41.   
  42.     if((status = msgrcv(msgid, (struct msgbuf*)&rcvmsg, 128, 200, IPC_NOWAIT)) == -1)  
  43.         err_quit("msgrcv error");  
  44.   
  45.     printf("Recevied message:\n%s\n", rcvmsg.msgtext);  
  46.     msgctl(msgid, IPC_RMID, 0);  
  47.     exit(0);  
  48. }  
  49. key_t MakeKey(const char *pathname)  
  50. {  
  51.     int fd;  
  52.     if((fd = open(pathname, O_CREAT, 0666)) < 0)  
  53.         err_quit("open error");  
  54.     close(fd);  
  55.   
  56.     return ftok(pathname, 0);  
  57. }  

输出结果:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. $ ./msg  
  2. Recevied message:  
  3. test message: Wellcome.  
  4. Recevied message:  
  5. test message: goodbye.  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值