v33.03 鸿蒙内核源码分析(消息队列) | 进程间如何异步传递大数据 原创
子在川上,曰:“逝者如斯夫!不舍昼夜。” 《论语》:子罕篇
百篇博客系列篇.本篇为:
v33.xx 鸿蒙内核源码分析(消息队列篇) | 进程间如何异步传递大数据
进程通讯相关篇为:
- v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志
- v27.05 鸿蒙内核源码分析(互斥锁) | 比自旋锁丰满的互斥锁
- v28.04 鸿蒙内核源码分析(进程通讯) | 九种进程间通讯方式速揽
- v29.05 鸿蒙内核源码分析(信号量) | 谁在负责解决任务的同步
- v30.07 鸿蒙内核源码分析(事件控制) | 任务间多对多的同步方案
- v33.03 鸿蒙内核源码分析(消息队列) | 进程间如何异步传递大数据
本篇说清楚消息队列
读本篇之前建议先读鸿蒙内核源码分析(总目录).
基本概念
-
队列又称消息队列,是一种常用于任务间通信的数据结构。队列接收来自任务或中断的不固定长度消息,并根据不同的接口确定传递的消息是否存放在队列空间中。
-
任务能够从队列里面读取消息,当队列中的消息为空时,挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。任务也能够往队列里写入消息,当队列已经写满消息时,挂起写入任务;当队列中有空闲消息节点时,挂起的写入任务被唤醒并写入消息。如果将读队列和写队列的超时时间设置为0,则不会挂起任务,接口会直接返回,这就是非阻塞模式。
-
消息队列提供了异步处理机制,允许将一个消息放入队列,但不立即处理。同时队列还有缓冲消息的作用。
-
队列用于任务间通信,可以实现消息的异步处理。同时消息的发送方和接收方不需要彼此联系,两者间是解耦的。
队列特性
- 消息以先进先出的方式排队,支持异步读写。
- 读队列和写队列都支持超时机制。
- 每读取一条消息,就会将该消息节点设置为空闲。
- 发送消息类型由通信双方约定,可以允许不同长度(不超过队列的消息节点大小)的消息。
- 一个任务能够从任意一个消息队列接收和发送消息。
- 多个任务能够从同一个消息队列接收和发送消息。
- 创建队列时所需的队列空间,默认支持接口内系统自行动态申请内存的方式,同时也支持将用户分配的队列空间作为接口入参传入的方式。
消息队列长什么样?
解读
- 和进程,线程,定时器一样,消息队列也由全局统一的消息队列池管理,池有多大?默认是1024
- 鸿蒙内核对消息的总个数有限制,
queueLen
消息总数的上限,在创建队列的时候需指定,不能更改. - 对每个消息的长度也有限制,
queueSize
规定了消息的大小,也是在创建的时候指定. - 为啥要指定总个数和每个的总大小,是因为内核一次性会把队列的总内存(
queueLen
*queueSize
)申请下来,确保不会出现后续使用过程中内存不够的问题出现,但同时也带来了内存的浪费,因为很可能大部分时间队列并没有跑满. - 队列的读取由
queueHead
,queueTail
管理,Head表示队列中被占用的消息节点的起始位置。Tail表示被占用的消息节点的结束位置,也是空闲消息节点的起始位置。队列刚创建时,Head和Tail均指向队列起始位置 - 写队列时,根据readWriteableCnt[1]判断队列是否可以写入,不能对已满(readWriteableCnt[1]为0)队列进行写操作。写队列支持两种写入方式:向队列尾节点写入,也可以向队列头节点写入。尾节点写入时,根据Tail找到起始空闲消息节点作为数据写入对象,如果Tail已经指向队列尾部则采用回卷方式。头节点写入时,将Head的前一个节点作为数据写入对象,如果Head指向队列起始位置则采用回卷方式。
- 读队列时,根据readWriteableCnt[0]判断队列是否有消息需要读取,对全部空闲(readWriteableCnt[0]为0)队列进行读操作会引起任务挂起。如果队列可以读取消息,则根据Head找到最先写入队列的消息节点进行读取。如果Head已经指向队列尾部则采用回卷方式。
- 删除队列时,根据队列ID找到对应队列,把队列状态置为未使用,把队列控制块置为初始状态。如果是通过系统动态申请内存方式创建的队列,还会释放队列所占内存。
- 留意readWriteList,这又是两个双向链表, 双向链表是内核最重要的结构体,牢牢的寄生在宿主结构体上.readWriteList上挂的是未来读/写消息队列的任务列表.
初始化队列
解读
- 初始队列模块,对几个全局变量赋值,创建消息队列池,所有池都是常驻内存,关于池后续有专门的文章整理,到目前为止已经解除到了进程池,任务池,定时器池,队列池,==
- 将
LOSCFG_BASE_IPC_QUEUE_LIMIT
个队列挂到空闲链表g_freeQueueList
上,供后续分配和回收.熟悉内核全局资源管理的对这种方式应该不会再陌生.
创建队列
解读
- 创建和初始化一个
LosQueueCB
- 动态分配内存来保存消息内容,
LOS_MemAlloc(m_aucSysMem1, (UINT32)len * msgSize);
msgSize = maxMsgSize + sizeof(UINT32);
头四个字节放消息的长度,但消息最大长度不能超过maxMsgSize
readWriteableCnt
记录读/写队列的数量,独立计算readWriteList
挂的是等待读取队列的任务链表 将在OsTaskWait(&queueCB->readWriteList[readWrite], timeout, TRUE);
中将任务挂到链表上.
关键函数OsQueueOperate
队列的读写都要经过 OsQueueOperate
解读
queueID
指定操作消息队列池中哪个消息队列operateType
表示本次是是读/写消息bufferAddr
,bufferSize
表示如果读操作,用buf接走数据,如果写操作,将buf写入队列.timeout
只用于当队列中没有读/写内容时的等待.- 当读消息时发现队列中没有可读的消息,此时timeout决定是否将任务挂入等待读队列任务链表
- 当写消息时发现队列中没有空间用于写的消息,此时timeout决定是否将任务挂入等待写队列任务链表
if (!LOS_ListEmpty(&queueCB->readWriteList[!readWrite]))
最有意思的是这行代码.- 在一次读消息完成后会立即唤醒写队列任务链表的任务,因为读完了就有了剩余空间,等待写队列的任务往往是因为没有空间而进入等待状态.
- 在一次写消息完成后会立即唤醒读队列任务链表的任务,因为写完了队列就有了新消息,等待读队列的任务往往是因为队列中没有消息而进入等待状态.
编程实例
创建一个队列,两个任务。任务1调用写队列接口发送消息,任务2通过读队列接口接收消息。
- 通过LOS_TaskCreate创建任务1和任务2。
- 通过LOS_QueueCreate创建一个消息队列。
- 在任务1 send_Entry中发送消息。
- 在任务2 recv_Entry中接收消息。
- 通过LOS_QueueDelete删除队列。
结果验证
总结
- 消息队列解决任务间大数据的传递
- 以一种异步,解耦的方式实现任务通讯
- 全局由消息队列池统一管理
- 在创建消息队列时申请内存块存储消息内存.
- 读/写操作统一管理,分开执行,A任务
读/写
完消息后会立即唤醒等待写/读
的B任务.
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
楼主不愧是你,吸睛标题+干货文章+俏皮顺口溜“51.c.h.o”+神一般的高产更新率
哈哈 , 总结 hin 到位, 这么好的文章,怎么还不被推荐呢 ?
外链太多?[偷笑]
没办法呀, 这是模板统一生成的, 没有API接口同步,靠人工同步费时又没技术含量很麻烦. 不过是真感谢平台的支持. 51CHO 越来越好.
v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 51 .c .h .o
51cto上漏了这一篇?
51漏了很多篇,更新太麻烦, 可前往 weharmony.gitee.io
请接受我的膜拜!
你一样可以的哦, 兴趣是最好的老师. : )