7.3.2消息队列

一个或多个进程可向消息队列写入消息,而一个或多个进程可从消息队列中读取消息,这种进程间通讯机制通常使用在客户/服务器模型中,客户向服务器发送请求消息,服务器读取消息并执行相应请求。在许多微内核结构的操作系统中,内核和各组件之间的基本通讯方式就是消息队列。例如,在 MINIX 操作系统中,内核、I/O 任务、服务器进程和用户进程之间就是通过消息队列实现通讯的。

Linux中的消息可以被描述成在内核地址空间的一个内部链表,每一个消息队列由一个IPC的标识号唯一的标识。Linux 为系统中所有的消息队列维护一个 msgque 链表,该链表中的每个指针指向一个 msgid_ds 结构,该结构完整描述一个消息队列。

1. 数据结构

 

(1)消息缓冲区(msgbuf)

我们在这里要介绍的第一个数据结构是msgbuf结构,可以把这个特殊的数据结构看成一个存放消息数据的模板,它在include/linux/msg.h中声明,描述如下:

/* msgsnd 和msgrcv系统调用使用的消息缓冲区*/

struct msgbuf {

long mtype;         /* 消息的类型,必须为正数 */

char mtext[1];      /* 消息正文 */

};

注意:对于消息数据元素(mtext),不要受其描述的限制。实际上,这个域(mtext)不仅能保存字符数组,而且能保存任何形式的任何数据。这个域本身是任意的,因为这个结构本身可以由应用程序员重新定义:

struct my_msgbuf {

long    mtype;          /*消息类型 */

long    request_id;     /*请求识别号 */

struct  client info;    /*客户消息结构 */

};

我们看到,消息的类型还是和前面一样,但是结构的剩余部分由两个其它的元素代替,而且有一个是结构。这就是消息队列的优美之处,内核根本不管传送的是什么样的数据,任何信息都可以传送。

但是,消息的长度还是有限制的,在Linux中,给定消息的最大长度在include/linux/msg.h中定义如下:

#define MSGMAX  8192    /* max size of message (bytes) */

消息总的长度不能超过8192字节,包括mtype域,它是4字节长。

(2)消息结构(msg)

内核把每一条消息存储在以msg结构为框架的队列中,它在include/ linux/msg.h中定义如下:

struct msg {

struct msg *msg_next;   /* 队列上的下一条消息 */

long  msg_type;          /*消息类型*/

char *msg_spot;         /* 消息正文的地址 */

short msg_ts;           /* 消息正文的大小 */

};

注意:msg_next是指向下一条消息的指针,它们在内核地址空间形成一个单链表。

(3)消息队列结构(msgid_ds)

当在系统中创建每一个消息队列时,内核创建、存储及维护这个结构的一个实例。

/* 在系统中的每一个消息队列对应一个msqid_ds结构 */

struct msqid_ds {

struct ipc_perm msg_perm;

struct msg *msg_first;    /*队列上第一条消息,即链表头*/

struct msg *msg_last;    /* 队列中的最后一条消息,即链表尾 */

time_t msg_stime;        /* 发送给队列的最后一条消息的时间 */

time_t msg_rtime;            /*从消息队列接收到的最后一条消息的时间 */

time_t msg_ctime;             /*最后修改队列的时间*/

ushort msg_cbytes;          /*队列上所有消息总的字节数 */

ushort msg_qnum;          /*在当前队列上消息的个数 */

ushort msg_qbytes;        /*队列最大的字节数 */

ushort msg_lspid;           /*发送最后一条消息的进程的pid */

ushort msg_lrpid;           /*接收最后一条消息的进程的pid */

};

2. 系统调用: msgget()

为了创建一个新的消息队列,或存取一个已经存在的队列,要使用msgget()系统调用。

原型: int msgget ( key_t key, int msgflg );

返回: 成功,则返回消息队列识别号,失败,则返回-1,

semget()中的第一个参数是键, 这个键值要与现有的键值进行比较,现有的键值指在内核中已存在的其它消息队列的键值。对消息队列的打开或存取操作依赖于msgflg参数的取值:

IPC_CREAT : 如果这个队列在内核中不存在,则创建它。

IPC_EXCL :当与IPC_CREAT一起使用时,如果这个队列已存在,则创建失败。

如果IPC_CREAT单独使用,semget()为一个新创建的消息队列返回标识号,或者返回具有相同键值的已存在队列的标识号。如果IPC_EXCL与IPC_CREAT一起使用,要么创建一个新的队列,要么对已存在的队列返回-1。IPC_EXCL单独是没有用的,当与IPC_CREAT结合起来使用时,可以保证新创建队列的打开和存取。

与文件系统的存取权限一样,每一个IPC对象也具有存取权限,因此,可以把一个八进制与掩码或,形成对消息队列的存取权限。

让我们来创建一个打开或创建消息队列的函数:

int open_queue( key_t keyval )

{

int     qid;

if((qid = msgget( keyval, IPC_CREAT | 0660 )) == -1)

{

return(-1);

}

return(qid);

}

注意,这个例子显式地用了0660权限。这个函数要么返回一个消息队列的标识号,要么返回-1而出错。键值作为唯一的参数必须传递给它。

3. 系统调用: msgsnd()

一旦我们有了队列识别号,我们就可以在这个队列上执行操作。要把一条消息传递给一个队列,你必须用msgsnd()系统调用。

原型:int msgsnd ( int msqid, struct msgbuf *msgp, int msgsz, int msgflg );

返回:成功为0, 失败为-1。

msgsnd()的第一个参数是队列识别号,由msgget()调用返回。第二个参数msgp是一个指针,指向我们重新声明和装载的消息缓冲区。msgsz参数包含了消息以字节为单位的长度,其中包括了消息类型的4个字节。

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

IPC_NOWAIT :如果消息队列满,消息不写到队列中,并且控制权返回给调用进程(继续执行)。如果不指定IPC_NOWAIT,调用进程将挂起(阻塞)直到消息被写到队列中。

让我们来看一个发送消息的简单函数:

int send_message( int qid, struct mymsgbuf *qbuf )

{

int     result, length;

/* mymsgbuf结构的实际长度 */

length = sizeof(struct ) - sizeof(long);

if((result = msgsnd( qid, qbuf, length, 0)) == -1)

{

return(-1);

}

return(result);

}

这个小函数试图把缓冲区qbuf中的消息,发送给队列识别号为qid的消息队列。

现在,我们在消息队列里有了一条消息,可以用ipcs命令来看你队列的状态。如何从消息队列检索消息,可以用msgrcv()系统调用。

4.系统调用:msgrcv()

原型:int msgrcv ( int msqid, struct msgbuf *msgp, int msgsz, long mtype, int msgflg );

返回值:成功,则为拷贝到消息缓冲区的字节数,失败为-1。

很明显,第一个参数用来指定要检索的队列(必须由msgget()调用返回),第二个参数(msgp)是存放检索到消息的缓冲区的地址,第三个参数(msgsz)是消息缓冲区的大小,包括消息类型的长度(4字节)。

第四个参数(mtype)指定了消息的类型。内核将搜索队列中相匹配类型的最早的消息,并且返回这个消息的一个拷贝,返回的消息放在由msgp参数指向的地址。这里存在一个特殊的情况,如果传递给mytype参数的值为0,就可以不管类型,只返回队列中最早的消息。

如果传递给参数msgflg的值为IPC_NOWAIT,并且没有可取的消息,那么给调用进程返回ENOMSG错误消息,否则,调用进程阻塞,直到一条消息到达队列并且满足msgrcv()的参数。如果一个客户正在等待消息,而队列被删除,则返回EIDRM。如果当进程正在阻塞,并且等待一条消息到达但捕获到了一个信号,则返回EINTR。

让我们来看一个从我们已建的消息队列中检索消息的例子

int read_message( int qid, long type, struct mymsgbuf *qbuf )

{

int     result, length;

/* 计算mymsgbuf结构的实际大小*/

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

if((result = msgrcv( qid, qbuf, length, type,  0)) == -1)

{

return(-1);

}

return(result);

}

当从队列中成功地检索到消息后,这个消息将从队列删除。

《消息队列》函数讲解相关推荐

  1. linux 声明消息函数,linux消息队列函数--解析

    #include www.2cto.com --------------------------------------------------------------------- 1.系统建立IP ...

  2. FreeRTOS — 消息队列

    以下内容转载自安富莱电子:http://forum.armfly.com/forum.php FreeRTOS 的一个重要的通信机制----消息队列,消息队列在实际项目中应用较多. 1.消息队列 1. ...

  3. ESP32使用freeRTOS的消息队列

    零. 声明 本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下: 第一篇:ESP-IDF基本介绍,主要会涉及模组,芯片,开发板的介绍,环境搭建,程序编译下载,启动流程等一些基本的操作,让你对 ...

  4. 智能家居DIY连载教程(2)——在实际项目中运用消息队列与邮箱

    前言 千呼万唤始出来,智能家居 DIY 教程连载第二篇终于登场了!本文将重点给大家介绍如何将消息队列与邮箱运用到实际项目中去.一起来看看吧~ DIY 回顾上期: 1.智能家居DIY连载教程(1)--如 ...

  5. ucosii事件控制块------消息邮箱与消息队列

    UCOSII 使用叫做事件控制块(ECB)的数据结构来描述诸如信号量.邮箱(消息邮箱)和消息队列这些事件 #define OS_EVENT_EN (((OS_Q_EN > 0u) &&a ...

  6. 进程间通信的方式(三):消息队列

     消息队列是在两个不相关进程间传递数据的一种简单.高效方式,她独立于发送进程.接受进程而存在.消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法.每个数据块都被认为是一个管道,接收进程可以独 ...

  7. freeRtos学习笔(4)消息队列

    freeRtos学习笔记 freeRtos消息队列 为什么要用消息队列 消息队列可以在任务与任务间,中断与任务间传递信息.为什么不用全局数组?全局数组也可以传递信息,但是和消息队列相比,消息队列有一下 ...

  8. stm32 ucosii消息队列 串口_正点原子STM32F407探索者开发板资料连载第六十三章 UCOSII 实验...

    1)实验平台:alientek 阿波罗 STM32F767 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 http://weix ...

  9. 【Linux系统编程】进程间通信之消息队列

    00. 目录 文章目录 00. 目录 01. 消息队列概述 02. 消息队列相关函数 03. 消息队列读写操作 04. 测试代码 05. 附录 01. 消息队列概述 消息队列提供了一种在两个不相关的进 ...

  10. 进程间通信:消息队列概念及代码

    前言 接下讨论的IPC机制,它们最初由System V版本的Unix引入.由于这些机制都出现在同一个版本中并且有着相似的编程接口,所以它们被称为System V IPC机制.接下来的内容包括: 信号量 ...

最新文章

  1. Python读excel——xlrd
  2. 关于Ubuntu运行级别、开机启动脚本的说明
  3. Python程序开发——第五章 函数
  4. c语言插入排序_还有这种操作?C语言插入排序算法,一点就透
  5. 云上快速搭建Serverless AI实验室
  6. python3flask教程_全面的Flask教程[3大部分]
  7. 微软2010年1月安全公告 Windows2000独领风骚
  8. MongoDB学习笔记—02 MongoDB入门
  9. [USACO13OPEN]What's Up With Gravityp【最短路 / SPFA】
  10. android焦点动画,Android编程中PopupWindow的用法分析【位置、动画、焦点】
  11. 聊聊 Material Design 里,阴影的那些事儿!
  12. 手把手教你做短视频去水印微信小程序(4-解析结果页)
  13. 希赛软考学习包,助您快通过软考考试
  14. 【Python教你一招】用Python实现黑客帝国代码雨效果(3种方式)
  15. 算法基础知识总结(数学知识)
  16. NYOJ-54小明的存钱计划
  17. 软件:汽车的灵魂操盘手
  18. c语言中最大公约数的编程,C语言中最大公约数求法
  19. 杨辉三角杨辉三角 || (JavaScript)
  20. Vue——vue3+ts

热门文章

  1. java数据结构基础名词解释
  2. windows 开启mysql日志记录_Windows下MySQL开启历史记录
  3. TodoMVC中的Backbone+MarionetteJS+RequireJS例子源码分析之三 Views
  4. [Swift]LeetCode79. 单词搜索 | Word Search
  5. nightwatch系列教程03——开发者指南:运行你的测试脚本
  6. Perl重命名当前目录下的文件
  7. Linux中的中断管理机制
  8. 为什么将表格的method改为post后就无法工作_不用再等后端的接口啦!这个开源项目花 2 分钟就能模拟出后端接口...
  9. matlab代码转换成python_[Python]40行代码实现公式转换成图片,手把手教你从模仿到实现...
  10. MySQL深度剖析之事务隔离级别和锁机制(2021)