博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux IPC实践(4) --System V消息队列(1)
阅读量:5937 次
发布时间:2019-06-19

本文共 4927 字,大约阅读时间需要 16 分钟。

消息队列概述

   消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法(仅局限于本机);

   每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值.

   消息队列也有管道一样的不足: (1)每个消息的最长字节数的上限(MSGMAX); (2)系统中消息队列的总条数也有一个上限(MSGMNI); (3)每个消息队列所能够保存的总字节数是有上限的(MSGMNB) .

 

查看系统限制

   cat /proc/sys/kernel/msgmax  #最大消息长度限制

   cat /proc/sys/kernel/msgmnb  #消息队列总的字节数

   cat /proc/sys/kernel/msgmni  #消息条目数

 

 

管道 vs. 消息队列

管道

消息

流管道

有边界

先进先出

可以后进先出

 

IPC对象数据结构

[cpp]
  1. //内核为每个IPC对象维护一个数据结构  
  2. struct ipc_perm  
  3. {  
  4.     key_t          __key;       /* Key supplied to msgget(2) */  
  5.     uid_t          uid;         /* Effective UID of owner */  
  6.     gid_t          gid;         /* Effective GID of owner */  
  7.     uid_t          cuid;        /* Effective UID of creator */  
  8.     gid_t          cgid;        /* Effective GID of creator */  
  9.     unsigned short mode;        /* Permissions */  
  10.     unsigned short __seq;       /* Sequence number */  
  11. };  
[cpp]
  1. //消息队列特有的结构  
  2. struct msqid_ds  
  3. {  
  4.     struct ipc_perm msg_perm;     /* Ownership and permissions 各类IPC对象所共有的数据结构*/  
  5.     time_t          msg_stime;    /* Time of last msgsnd(2) */  
  6.     time_t          msg_rtime;    /* Time of last msgrcv(2) */  
  7.     time_t          msg_ctime;    /* Time of last change */  
  8.     unsigned long   __msg_cbytes; /* Current number of bytes in queue (nonstandard) 消息队列中当前所保存的字节数 */  
  9.     msgqnum_t       msg_qnum;     /* Current number of messages in queue 消息队列中当前所保存的消息数 */  
  10.     msglen_t        msg_qbytes;   /* Maximum number of bytes allowed in queue 消息队列所允许的最大字节数 */  
  11.     pid_t           msg_lspid;    /* PID of last msgsnd(2) */  
  12.     pid_t           msg_lrpid;    /* PID of last msgrcv(2) */  
  13. };  

消息队列在内核中的表示

 

消息在消息队列中是以链表形式保存的, 每个节点的类型类似如下:

[cpp]
  1. struct msq_Node  
  2. {  
  3.     Type msq_type;  //类型  
  4.     Length msg_len; //长度  
  5.     Data msg_data;  //数据  
  6.   
  7.     struct msg_Node *next;  
  8. };  

消息队列API

[cpp]
  1. #include <sys/types.h>  
  2. #include <sys/ipc.h>  
  3. #include <sys/msg.h>  
  4. int msgget(key_t key, int msgflg);  
  5. int msgctl(int msqid, int cmd, struct msqid_ds *buf);  
  6. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);  
  7. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);  

msgget

功能:用来创建和访问一个消息队列

[cpp]
  1. int msgget(key_t key, int msgflg);  

参数:

  key: 某个消息队列的名字

  msgflg:由九个权限标志构成,如0644,它们的用法和创建文件时使用的mode模式标志是一样的(但是消息队列没有x(执行)权限)

返回值:

  成功返回消息队列编号,即该消息队列的标识码;失败返回-1

 

msgget调用关系图

[cpp]
  1. /** 示例1: 在msgflg处指定IPC_CREAT, 如果不存在该消息队列, 则创建之**/  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     //指定IPC_CREAT,如果不存在, 则创建消息队列  
  5.     int msgid = msgget(1234, 0666|IPC_CREAT);  
  6.     if (msgid == -1)  
  7.         err_exit("msgget error");  
  8.     cout << "msgget success" << endl;  
  9. }  
[cpp]
  1. /** 示例2:IPC_CREAT|IPC_EXCL, 如果该消息队列已经存在, 则返回出错 **/  
  2. int main(int argc, char *argv[])  
  3. {  
  4.     //指定IPC_EXCL, 如果已经存在,则报告文件已经存在(错误)  
  5.     int msgid = msgget(1234, 0666|IPC_CREAT|IPC_EXCL);  
  6.     if (msgid == -1)  
  7.         err_exit("msgget error");  
  8.     cout << "msgget success" << endl;  
  9. }  
[cpp]
  1. /**示例3:将key指定为IPC_PRIVATE(值为0) 
  2. 将key指定为IPC_PRIVATE之后,则msgget就一定会创建一个新的消息队列, 
  3. 而且每次创建的消息队列的描述符都是不同的! 因此, 除非将MessageID(key)传送给其他进程(除非有关联的进程),其他进程也无法使用该消息队列(血缘fork除外) 
  4. 因此, IPC_PRIVATE创建的消息队列,只能用在与当前进程有关系的进程中使用! 
  5. **/  
  6. int main(int argc, char *argv[])  
  7. {  
  8.     //指定IPC_PRIVATE  
  9.     int msgid = msgget(IPC_PRIVATE, 0666|IPC_CREAT|IPC_EXCL);  
  10.     if (msgid == -1)  
  11.         err_exit("msgget error");  
  12.     cout << "msgget success" << endl;  
  13. }  
[cpp]
  1. /** 示例4: 仅打开消息队列时, msgflg选项可以直接忽略(填0), 此时是以消息队列创建时的权限进行打开 
  2. **/  
  3. int main(int argc, char *argv[])  
  4. {  
  5.     int msgid = msgget(1234, 0);  
  6.     if (msgid == -1)  
  7.         err_exit("msgget error");  
  8.     cout << "msgget success" << endl;  
  9.     cout << "msgid = " << msgid << endl;  
  10. }  
[cpp]
  1. //示例5:低权限创建,高权限打开  
  2. int main()  
  3. {  
  4.     //低权限创建  
  5.     int msgid = msgget(0x255,0444 | IPC_CREAT);  
  6.     if (msgid < 0)  
  7.         err_exit("mesget error");  
  8.     else  
  9.         cout << "Create Mes OK, msgid = " << msgid << endl;  
  10.   
  11.     //高权限打开  
  12.     msgid = msgget(0x255,0644 | IPC_CREAT);  
  13.     if (msgid < 0)  
  14.         err_exit("mesget error");  
  15.     else  
  16.         cout << "Create Mes OK, msgid = " << msgid << endl;  
  17. }  

msgctl函数

功能:获取/设置消息队列的信息

[cpp]
  1. int msgctl(int msqid, int cmd, struct msqid_ds *buf);  

参数:

   msqid: 由msgget函数返回的消息队列标识码

   cmd:是将要采取的动作(见下)

 

cmd:将要采取的动作(有三个可取值),分别如下:

[cpp]
  1. /** 示例1: IPC_RMID, 删除消息队列 
  2. 注意: 消息队列并没有运用”引用计数”的功能 
  3. **/  
  4. int main()  
  5. {  
  6.     int msgid = msgget(1234, 0);  
  7.     if (msgid == -1)  
  8.         err_exit("msgget error");  
  9.     if (msgctl(msgid, IPC_RMID, NULL) == -1)  
  10.         err_exit("msgctl IPC_RMID error");  
  11.     cout << "msgctl IPC_RMID success" << endl;  
  12. }  
[cpp]
  1. /** 示例2: IPC_STAT 
  2. **/  
  3. int main()  
  4. {  
  5.     int msgid = msgget(0x255, 0666|IPC_CREAT);  
  6.     if (msgid == -1)  
  7.         err_exit("msgget error");  
  8.   
  9.     struct msqid_ds buf;  
  10.     if (msgctl(msgid,IPC_STAT,&buf) == -1)  
  11.         err_exit("msgctl error");  
  12.   
  13.     printf("buf.msg_perm.mode = %o\n",buf.msg_perm.mode);   //%o以八进制打印  
  14.     printf("buf.__key = %x\n", buf.msg_perm.__key);         //%x以十六进制打印  
  15.     cout << "buf.__msg_cbytes = " << buf.__msg_cbytes << endl;  
  16.     cout << "buf.msg_qbytes = " << buf.msg_qbytes << endl;  
  17.     cout << "buf.msg_lspid = " << buf.msg_lspid << endl;  
  18. }  
[cpp]
  1. /** 实践:IPC_SET,一般需要先获取,然后再设置 
  2. **/  
  3. int main()  
  4. {  
  5.     int msgid = msgget(0x255, 0);  
  6.     if (msgid == -1)  
  7.         err_exit("msgget error");  
  8.   
  9.     //获取消息队列的属性  
  10.     struct msqid_ds buf;  
  11.     if (msgctl(msgid,IPC_STAT,&buf) == -1)  
  12.         err_exit("msgctl error");  
  13.   
  14.     //设置消息队列的属性  
  15.     buf.msg_perm.mode = 0600;  
  16.     if (msgctl(msgid, IPC_SET, &buf) == -1)  
  17.         err_exit("msgctl error");  
  18.   
  19.     //获取并打印  
  20.     bzero(&buf, sizeof(buf));  
  21.     if (msgctl(msgid, IPC_STAT, &buf) == -1)  
  22.         err_exit("msgctl IPC_STAT error");  
  23.     printf("mode = %o\n", buf.msg_perm.mode);  
  24. }  

附-查看系统中的IPC对象

     ipcs

   删除消息队列

     ipcrm -q [msqid]

  或  ipcrm -Q [key] #如果key不等于0的话

你可能感兴趣的文章
MySQL字符串函数substring:字符串截取
查看>>
ystep jQuery流程、步骤插件
查看>>
JQuery+ajax+jsonp 跨域访问
查看>>
现代软件工程 第七章 【MSF】练习与讨论
查看>>
Android网络之数据解析----SAX方式解析XML数据
查看>>
Java递归列出所有文件和文件夹
查看>>
[关于SQL]查询成绩都大于80分的学生
查看>>
Delphi(Tuxedo,BDE,ADO)三合一数据集组件HsTxQuery
查看>>
java之ibatis数据缓存
查看>>
纪念逝去的岁月——C/C++选择排序
查看>>
第6章 数组----复制数组
查看>>
STL区间成员函数及区间算法总结
查看>>
“TNS-03505:无法解析名称”问题解决一例
查看>>
二分图
查看>>
BootStrap学习(2)
查看>>
JVM源码分析之FinalReference完全解读
查看>>
LeetCode - Longest Common Prefix
查看>>
Android图片处理
查看>>
2015年第21本:万万没想到,用理工科思维理解世界
查看>>
大家谈谈公司里的项目经理角色及职责都是干什么的?
查看>>