消息队列的概念

消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述。队列头中包含了该队列的大量信息,包括消息队列的键值、用户ID、组ID、消息数目、读写进程ID等。其定义如下:

struct msg_queue
{struct ipc_perm q_perm;time_t q_stime;     // last msgsnd timetime_t q_rtime;     // last msgrcv timetime_t q_ctime;     // last change timeunsigned long q_cbytes;      // current number of bytes on queueunsigned long q_qnum;        // number of message in queueunsigned long q_qbytes;      // max number of bytes on queuepid_t q_lspid;     // pid of last msgsndpid_t q_lrpid;     // last receive pidstruct list_head q_messages;struct list_head q_receives;struct list_head q_senders;
};

结构体msqid_ds用来设置或返回消息队列的信息,定义如下:

// 摘自所用ubuntu18.04电脑中的/usr/include/i386-linux-gnu/bits/msq.h
struct msqid_ds
{struct ipc_perm msg_perm;     /* structure describing operation permission */__time_t msg_stime;           /* time of last msgsnd command */
#ifndef __x86_64__unsigned long int __glibc_reserved1;
#endif__time_t msg_rtime;           /* time of last msgrcv command */
#ifndef __x86_64__unsigned long int __glibc_reserved2;
#endif__time_t msg_ctime;           /* time of last change */
#ifndef __x86_64__unsigned long int __glibc_reserved3;
#endif__syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */msgqnum_t msg_qnum;           /* number of messages currently on queue */msglen_t msg_qbytes;          /* max number of bytes allowed on queue */__pid_t msg_lspid;            /* pid of last msgsnd() */__pid_t msg_lrpid;            /* pid of last msgrcv() */__syscall_ulong_t __glibc_reserved4;__syscall_ulong_t __glibc_reserved5;
};

消息队列的创建与打开

消息队列具有一个唯一的键值,或称引用标识符、消息队列的ID号,通过使用ftok()函数获取,函数原型:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(char *pathname, char proj);

获取成功返回消息队列的键值,失败返回-1。

参数pathname为一任意存在的路径名,参数proj为1~255之间的任一数字,ftok根据路径名,提取文件信息,再根据这些文件信息及proj的值合成key。(注:此段参考:ftok()函数深度解析)。

ftok()函数并不直接对消息队列操作,生成的键值用于msgget()函数使用,该函数用于创建或打开一个消息队列,其函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

运行成功则返回消息队列的引用标识符(ID),失败则返回-1。

参数keyftok()产生的键值,参数msgflg是一些标志位,可以取IPC_CREATIPC_EXCLIPC_NOWAIT或三者的逻辑或结果。

在以下两种情况下,msgget()将创建一个新的消息队列:

  • 如果没有消息队列与键值key相对应,且msgflg中包含了IPC_CREAT标志位
  • key参数为IPC_PRIVATE

消息队列的读写

消息队列传递的消息由两部分组成,包括消息类型所传的数据,用结构体struct msgbuf表示:

struct msgbuf
{long msgtype;char msgtext[1024];
};

msgtype成员代表消息类型,msgtext成员为消息内容,长度不一定是1024。对于发送端,首先预置一个这样的msgbuf缓冲区并写入消息类型和内容,然后调用相应的发送函数;对于接收端,首先分配一个msgbuf缓冲区,然后把消息读入缓冲区即可。

发送数据(写)

向消息队列发送数据使用msgsnd()函数,发送的一个消息数据会被添加到队列的末尾,函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *prt, size_t nbytes, int flags);

运行成功返回0,失败返回-1。参数msqid为消息队列的引用标识符(ID),参数prt为void型指针,指向要发送到的消息,参数nbytes为发送的消息的字节长度,参数flag用于指定消息队列满时的处理方法。

对发送消息来说,有意义的flags标志为IPC_NOWAIT,在消息队列没有足够的空间容纳要发送的数据时,设置了该标志,则msgsnd()函数立刻出错返回,否则发送消息的进程被阻塞,直至消息队列有空间或队列被删除时返回。

接收数据(读)

从消息队列接收数据使用msgrcv()函数,函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid, const void *prt, size_t nbytes, long type, int flags);

运行成功返回0,失败返回-1。

参数含义与msgsnd()函数类似,参数flag用于指定消息队列满时的处理方法,取值有3种以及3种的或结果,参数type表示接收的数据类型。

消息队列属性设置

消息队列的信息基本都保存在消息队列头中,可分配一个类似于消息队列头的结构struct msqid_ds来返回消息队列的属性,同样可以设置该数据结构。属性设置使用msgctl()函数,函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

运行成功返回0,失败返回-1。

函数msgctl()将对参数msqid标识的消息队列执行参数cmd所指的命令,包括3种命令:

  • IPC_STAT:用于获取消息队列信息,返回的信息存贮在参数buf
  • IPC_SET:用于设置消息队列的属性,要设置的属性存储在参数buf
  • PC_RMID:删除msqid标识的消息队列

编程示例

消息队列编程步骤:

  • 使用ftok()生成key
  • 使用msgget()创建/获取消息队列,返回值为队列标识符
  • 发送消息msgsnd()/接收消息msgrcv()
  • 消息队列属性与删除msgctl()

示例1

简单使用。

发送端,msg1_snd.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main()
{// generate keykey_t key = ftok(".", 100);if(key == -1){perror("ftoke failed");exit(1);}printf("key = %#xn", key);// create message queueint msgid = msgget(key, 0666|IPC_CREAT|IPC_EXCL);if(msgid == -1){perror("msgget failed");exit(2);}// send datamsgsnd(msgid, "hello world!n", 14, 0);printf("use Enter to destory the message queue!n");getchar();// detele message queueif(msgctl(msgid, IPC_RMID, NULL) == -1){perror("msgctl failed");exit(3);}return 0;
}

接收端,msg1_rcv.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main()
{// generate keykey_t key = ftok(".", 100);if(key == -1){perror("ftoke failed");exit(1);}printf("key = %#xn", key);// get message queueint msgid = msgget(key, 0);if(msgid == -1){perror("msgget failed");exit(2);}// read from the message queuechar buf[100] = {};msgrcv(msgid, buf, 100, 0, 0);printf("read from message queue:%sn", buf);return 0;
}

在一个shell中运行消息队列发送程序:

$ ./msg1_snd
key = 0x641102ed
use Enter to destory the message queue!

在另一个shell中运行消息队列接收程序:

$ ./msg1_rcv
key = 0x641102ed
read from message queue:hello world!

示例2

发送带消息类型的数据。

发送端,msg2_send.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>struct _msg
{long mtype;char buf[256];
}msg1,msg2;int main()
{// generate keykey_t key = ftok(".", 100);if(key == -1){perror("ftoke failed");exit(1);}printf("key = %#xn", key);// create message queueint msgid = msgget(key, 0666|IPC_CREAT);if(msgid == -1){perror("msgget failed");exit(2);}//send datamsg1.mtype = 2;strcpy(msg1.buf, "hello2");msgsnd(msgid, &msg1, sizeof(msg1.buf), 0);msg2.mtype = 1;strcpy(msg2.buf, "hello1");msgsnd(msgid, &msg2, sizeof(msg2.buf), 0);printf("use Enter to destory the message queue!n");getchar();// destroy the messsage queueif(msgctl(msgid, IPC_RMID, NULL) == -1){perror("msgctl failed");exit(3);}return 0;
}

接收端,msg2_rcv.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>struct _msg
{long mtype;char buf[256];
}msg1,msg2;int main()
{// generate keykey_t key = ftok(".", 100);if(key == -1){perror("ftoke failed");exit(1);}printf("key = %#xn", key);// get message queueint msgid = msgget(key, 0);if(msgid == -1){perror("msgget failed");exit(2);}// read from the message queueint res = msgrcv(msgid, &msg1, sizeof(msg1)-4, 0, 0);while(res != 1){printf("Message:%s, Type:%ldn", msg1.buf, msg1.mtype);res = msgrcv(msgid, &msg1, sizeof(msg1)-4, 0, 0);}return 0;
}

在一个shell中运行消息队列发送程序:

$ ./msg2_snd
key = 0x651102ed
use Enter to destory the message queue!

在另一个shell中运行消息队列接收程序:

$ ./msg2_rcv
key = 0x651102ed
Message:hello2, Type:2
Message:hello1, Type:1

示例3

消息队列的综合编程使用举例,msg_app.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>void msg_stat(int, struct msqid_ds);int main(void)
{int gflags, sflags, rflags;key_t key;int msgid;int reval;struct msgsbuf{int mtype;char mtext[10];}msg_sbuf;// sendstruct msgmbuf{int mtype;char mtext[10];}msg_rbuf;// receivrstruct msqid_ds msg_ginfo, msg_sinfo;// create keykey = ftok(".", 30);if(key == -1){printf("ftok failed!");return -1;}printf("key = %#xn", key);// create message queuegflags = IPC_CREAT|IPC_EXCL;msgid = msgget(key, 0666|gflags);if(msgid == -1){printf("msg create errorn");return -1;}elseprintf("msg create okn");// after create the message queue, show it's property, use msg_stat 1stprintf("n msg_stat1:");msg_stat(msgid, msg_ginfo);// send messagesflags = IPC_NOWAIT;msg_sbuf.mtype = 8;msg_sbuf.mtext[0] = 'a';msg_sbuf.mtext[1] = 'b';msg_sbuf.mtext[2] = 'c';reval = msgsnd(msgid, &msg_sbuf, sizeof(msg_sbuf.mtext), sflags);if(reval == -1)printf("message send errorn");elseprintf("message send okn");// after send a message, shoe it's property, use msg_stat 2stprintf("n msg_stat2:");msg_stat(msgid, msg_ginfo);// receive messagerflags = IPC_NOWAIT|MSG_NOERROR;reval = msgrcv(msgid, &msg_rbuf, 3, 0, rflags);if(reval == -1)printf("msg read errorn");// ===else{printf("read from msg queue %d bytesn", reval);printf("type:%d, message:%sn", msg_rbuf.mtype, msg_rbuf.mtext);}// use msg_stat 3stprintf("n msg_stat3:");msg_stat(msgid, msg_ginfo);// change message propertymsg_sinfo = msg_ginfo;msg_sinfo.msg_perm.uid = 8; // user IDmsg_sinfo.msg_perm.gid = 8; // group IDmsg_sinfo.msg_qbytes = 16388; // queue bytes 16384->16388reval = msgctl(msgid, IPC_SET, &msg_sinfo);if(reval == -1){printf("msg set info errorn");// ===}// use msg_stat 4stprintf("n msg_stat4:");msg_stat(msgid, msg_ginfo);// delete the message queuereval = msgctl(msgid, IPC_RMID, NULL);if(reval == -1){printf("unlink msg queue errorn");}return 0;
}void msg_stat(int msgid, struct msqid_ds msg_info)
{int reval;sleep(1);// get message propertyreval = msgctl(msgid, IPC_STAT, &msg_info);if(reval == -1){printf("get msg info errorn");return;}printf("n");printf("current number of bytes on queue is %ldn", msg_info.msg_cbytes);printf("number of message in queue is %ldn", msg_info.msg_qnum);printf("max number of bytes on queue is %ldn", msg_info.msg_qbytes);//printf("pid of last msgsnd is %dn", msg_info.msg_lspid); // last send opera                                                       te process's IDprintf("pid of last msgrcv is %dn", msg_info.msg_lrpid); // last receive op                                                       erate process's IDprintf("last msgsnd time is %s", ctime(&(msg_info.msg_stime))); // last send                                                        timeprintf("last msgrcv time is %s", ctime(&(msg_info.msg_rtime))); // last rece                                                       ive timeprintf("last change time is %s", ctime(&(msg_info.msg_ctime))); // last chan                                                       ge timeprintf("msg uid is %dn", msg_info.msg_perm.uid); // message queue user IDprintf("msg gid is %dn", msg_info.msg_perm.gid); // message queue group ID
}

编译后执行,需要sudo权限,否则change message property 步骤会失败:

$ sudo ./msg_app
[sudo] password for deeplearning:
key = 0x1e11060e
msg create okmsg_stat1:
current number of bytes on queue is 0
number of message in queue is 0
max number of bytes on queue is 16384
pid of last msgsnd is 0
pid of last msgrcv is 0
last msgsnd time is Thu Jan  1 08:00:00 1970
last msgrcv time is Thu Jan  1 08:00:00 1970
last change time is Wed Nov 27 10:40:41 2019
msg uid is 0
msg gid is 0message send okmsg_stat2:
current number of bytes on queue is 10
number of message in queue is 1
max number of bytes on queue is 16384
pid of last msgsnd is 3912
pid of last msgrcv is 0
last msgsnd time is Wed Nov 27 10:40:42 2019
last msgrcv time is Thu Jan  1 08:00:00 1970
last change time is Wed Nov 27 10:40:41 2019
msg uid is 0
msg gid is 0read from msg queue 3 bytestype:8, message:abcmsg_stat3:
current number of bytes on queue is 0
number of message in queue is 0
max number of bytes on queue is 16384
pid of last msgsnd is 3912
pid of last msgrcv is 3912
last msgsnd time is Wed Nov 27 10:40:42 2019
last msgrcv time is Wed Nov 27 10:40:43 2019
last change time is Wed Nov 27 10:40:41 2019
msg uid is 0
msg gid is 0msg set okmsg_stat4:
current number of bytes on queue is 0
number of message in queue is 0
max number of bytes on queue is 16388
pid of last msgsnd is 3912
pid of last msgrcv is 3912
last msgsnd time is Wed Nov 27 10:40:42 2019
last msgrcv time is Wed Nov 27 10:40:43 2019
last change time is Wed Nov 27 10:40:44 2019
msg uid is 8
msg gid is 8unlink msg queue ok

程序首先生成key并创建消息队列,第1次输出消息队列的属性信息,接着发送数据,第2次输出属性信息,然后接收数据,第3次输出属性信息,再然后修改属性,第4次输出属性信息,最后删除消息队列。

注意观察结果中的时间变化以及各种ID数值的变化。

参考:

  1. 《精通Linux C编程》- 程国钢
  2. 《Linux C编程完全解密》- 闫敬 吴淑坤

exit函数_Linux进程间通信详解(三) 消息队列及函数相关推荐

  1. Linux进程间通信详解(三) —— 消息队列及函数

    消息队列的概念 消息队列就是一个消息的链表,每个消息队列都有一个队列头,用结构struct msg_queue来描述.队列头中包含了该队列的大量信息,包括消息队列的键值.用户ID.组ID.消息数目.读 ...

  2. java 消息队列详解_Java消息队列-Spring整合ActiveMq的详解

    本篇文章主要介绍了详解Java消息队列-Spring整合ActiveMq ,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 1.概述 首先和大家一起回顾一下Java 消息服 ...

  3. 【Python养成】常用内置函数 — 2(详解25个内置函数)

    图片来自互联网 文章目录 前言 二.内置函数详解 1.函数:chr(x) 2.函数:dir([obj]) 3.函数:divmod(x,y) 4.函数:enumerate(sequence, [star ...

  4. python怎么画参数函数图像_详解pandas.DataFrame.plot() 画图函数

    首先看官网的DataFrame.plot( )函数 DataFrame.plot(x=None, y=None, kind='line', ax=None, subplots=False, share ...

  5. Linux进程间通信详解(四) —— 共享内存及函数

    共享内存的概念 共享内存是指多个进程可以把一段内存共同的内存映射到自己的进程空间中,从而实现数据的共享和传输,它是存在与内核级别的一种资源,是所有进程间通信中方式最快的一种. 在shell环境下可以使 ...

  6. 详解MQ消息队列及四大主流MQ的优缺点

             正文    前言近期有了想跳槽的打算,所以自己想巩固一下自己的技术,想了解一些面试比较容易加分的项,近期准备深入研究一下Redis和MQ这两样,这总体上都是为了解决服务器并发的原因, ...

  7. 【详解】消息队列和线程关系

    1.进程-线程-消息队列 简单的来说,什么是进程?什么是线程?打个比方,你的程序要执行,操作系统就会把你的exe文件加载到内存中,那就生成一个进程了(当然还包含分配到的资源等):对于线程,你可以理解成 ...

  8. python def函数报错详解_python所有内置函数的定义详解

    >>> def hello_world(): ...     print('Hello,world!')   # 注意函数体要有缩进 ... >>> hello_w ...

  9. C++笔记 二维数组作为函数的参数详解 三种传参的方法总结 注意要点总结

    文章目录 1.C/C++ 二维数组作为函数的参数 2.不合法写法总结 3.测试案例 3.1 传参方式1 3.2 传参方式2 3.3 传参方式3 4.运行结果 5.总结 1.C/C++ 二维数组作为函数 ...

最新文章

  1. 2019年,为什么Web前端工程师薪资越来越高?
  2. 美媒评全球十家增速最快IT办事公司 当当网居首
  3. hbase,根据前缀匹配进行搜索并分批次获取结果
  4. python csv 大文件_Python性能调优:JSON到CSV,大文件
  5. 3步理清Python数据分析关键点,新手必看
  6. caffe:无法读取文件cuda8.0.props
  7. eclipse 与 tomcat 的那些路径
  8. java导出pdf 含图片_java 生成PDF含图片和中文件实现代码
  9. 定义一个泛型为String类型的List集合,统计该集合中每个字符 (注意,不是字符串)出现的次数。例如:集合中有”abc”、”bcd”两个元素, 程序最终输出结果为:
  10. Linux pwd命令:显示当前路径
  11. 收藏 |彻底搞懂感受野的含义与计算
  12. 传智播客 Html基础知识学习笔记
  13. Installing Flex Data Services on JBoss
  14. Unity3D 物体运动小游戏后续
  15. 比较 Python(Python 与其他语言的比较)
  16. 问题二:用C++输出第一张图片
  17. C语言字符串类型转换为整型,c语言中将一个字符串转换到整型数据类型的函数是什么?...
  18. seaweedfs java api_seaweedfs代码详解
  19. 关键词抽取工具-THUtag 个人使用心得
  20. AMR 文件解析及编解码流程

热门文章

  1. sql crud_使用适用于SQL Server的Python SQL库执行CRUD操作
  2. C++Primer第四版 阅读笔记 第二部分 “容器和算法”
  3. Unity教程之-Unity Attribute的使用总结
  4. 剑指Offer_12_矩阵中的路径(参考问题:马踏棋盘)
  5. leetcode-Single Number II-137
  6. JavaScript中的逗号运算符
  7. C++ Notes(focus on c++)
  8. C#扩展方法奇思妙用高级篇一:改进 Scottgu 的 In 扩展
  9. DirectX9.03D Direct3D初始化
  10. 启动tomcat报错,Failed to start component