v30.07 鸿蒙内核源码分析(事件控制) | 任务间多对多的同步方案 原创
子曰:“吾有知乎哉?无知也。有鄙夫问于我,空空如也,我叩其两端而竭焉。” 《论语》:子罕篇
百篇博客系列篇.本篇为:
- [v30.xx 鸿蒙内核源码分析(事件控制篇) | 任务间多对多的同步方案
进程通讯相关篇为:
- v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志
- v27.05 鸿蒙内核源码分析(互斥锁) | 比自旋锁丰满的互斥锁
- v28.04 鸿蒙内核源码分析(进程通讯) | 九种进程间通讯方式速揽
- v29.05 鸿蒙内核源码分析(信号量) | 谁在负责解决任务的同步
- v30.07 鸿蒙内核源码分析(事件控制) | 任务间多对多的同步方案
- v33.03 鸿蒙内核源码分析(消息队列) | 进程间如何异步传递大数据
本篇说清楚事件(Event)
读本篇之前建议先读鸿蒙内核源码分析(总目录)其他篇.
官方概述
先看官方对事件的描述.
事件(Event)是一种任务间通信的机制,可用于任务间的同步。
多任务环境下,任务之间往往需要同步操作,一个等待即是一个同步。事件可以提供一对多、多对多的同步操作。
-
一对多同步模型:一个任务等待多个事件的触发。可以是任意一个事件发生时唤醒任务处理事件,也可以是几个事件都发生后才唤醒任务处理事件。
-
多对多同步模型:多个任务等待多个事件的触发。
鸿蒙提供的事件具有如下特点:
- 任务通过创建事件控制块来触发事件或等待事件。
- 事件间相互独立,内部实现为一个32位无符号整型,每一位标识一种事件类型。第25位不可用,因此最多可支持31种事件类型。
- 事件仅用于任务间的同步,不提供数据传输功能。
- 多次向事件控制块写入同一事件类型,在被清零前等效于只写入一次。
- 多个任务可以对同一事件进行读写操作。
- 支持事件读写超时机制。
再看事件图
注意图中提到了三个概念 事件控制块
事件
任务
接下来结合代码来理解事件模块的实现.
事件控制块长什么样?
简单是简单,就两个变量,如下:
uwEventID
:用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0表示该事件类型未发生、1表示该事件类型已经发生),一共31种事件类型,第25位系统保留。
stEventList
,这又是一个双向链表, 双向链表是内核最重要的结构体, 可前往 鸿蒙内核源码分析(总目录) 查看双向链表篇.
LOS_DL_LIST
像狗皮膏药一样牢牢的寄生在宿主结构体上stEventList
上挂的是所有等待这个事件的任务.
事件控制块<>事件<>任务 三者关系
一定要搞明白这三者的关系,否则搞不懂事件模块是如何运作的.
-
任务是事件的生产者,通过
LOS_EventWrite
,向外部广播发生了XX事件,并唤醒此前已在事件控制块中登记过的要等待XX事件发生的XX任务. -
事件控制块
EVENT_CB_S
是记录者,只干两件事件:1.
uwEventID
按位记录哪些事件发生了,它只是记录,怎么消费它不管的.2.
stEventList
记录哪些任务在等待事件,但任务究竟在等待哪些事件它也是不记录的 -
任务也是消费者,通过
LOS_EventRead
消费,只有任务自己清楚要以什么样的方式,消费什么样的事件.
先回顾下任务结构体LosTaskCB
对事件部分的描述如下:taskEvent
指向的就是EVENT_CB_S
eventMask
屏蔽掉 事件控制块 中的哪些事件eventMode
已什么样的方式去消费事件,三种读取模式-
所有事件(
LOS_WAITMODE_AND
):逻辑与,基于接口传入的事件类型掩码eventMask
,只有这些事件都已经发生才能读取成功,否则该任务将阻塞等待或者返回错误码。 -
任一事件(
LOS_WAITMODE_OR
):逻辑或,基于接口传入的事件类型掩码eventMask
,只要这些事件中有任一种事件发生就可以读取成功,否则该任务将阻塞等待或者返回错误码。 -
清除事件(
LOS_WAITMODE_CLR
):这是一种附加读取模式,需要与所有事件模式或任一事件模式结合使用(LOS_WAITMODE_AND | LOS_WAITMODE_CLR
或LOS_WAITMODE_OR | LOS_WAITMODE_CLR
)。在这种模式下,当设置的所有事件模式或任一事件模式读取成功后,会自动清除事件控制块中对应的事件类型位。
-
-
一个事件控制块
EVENT_CB_S
中的事件可以来自多个任务,多个任务也可以同时消费事件控制块中的事件,并且这些任务之间可以没有任何关系!
函数列表
事件可应用于多种任务同步场景,在某些同步场景下可替代信号量。
其中读懂 OsEventWrite
和 OsEventRead
就明白了事件模块.
事件初始化 -> LOS_EventInit
代码解读:
- 事件是共享资源,所以操作期间不能产生中断.
- 初始化两个记录者
uwEventID
stEventList
事件生产过程 -> OsEventWrite
代码解读:
-
给对应位贴上事件标签,
eventCB->uwEventID |= events;
注意uwEventID是按位管理的.每个位代表一个事件是否写入,例如uwEventID = 00010010
代表产生了 1,4 事件 -
循环从
stEventList
链表中取出等待这个事件的任务判断是否唤醒任务.OsEventResume
3.唤醒任务OsTaskWake
只是将任务重新加入就绪队列,需要立即申请一次调度 LOS_Schedule
.
事件消费过程 -> OsEventRead
代码解读:
- 事件控制块是给任务使用的, 任务给出读取一个事件的条件
eventMask
告诉系统屏蔽掉这些事件,对屏蔽的事件不感冒.eventMode
已什么样的方式去消费事件,是必须都满足给的条件,还是只满足一个就响应.- 条件给完后,自己进入等待状态
OsTaskWait
,等待多久timeout
决定,任务自己说了算. OsEventPoll
检测事件是否符合预期,啥意思?看下它的代码就知道了
编程实例
本实例实现如下流程。
示例中,任务Example_TaskEntry创建一个任务Example_Event,Example_Event读事件阻塞,Example_TaskEntry向该任务写事件。可以通过示例日志中打印的先后顺序理解事件操作时伴随的任务切换。
- 在任务Example_TaskEntry创建任务Example_Event,其中任务Example_Event优先级高于Example_TaskEntry。
- 在任务Example_Event中读事件0x00000001,阻塞,发生任务切换,执行任务Example_TaskEntry。
- 在任务Example_TaskEntry向任务Example_Event写事件0x00000001,发生任务切换,执行任务Example_Event。
- Example_Event得以执行,直到任务结束。
- Example_TaskEntry得以执行,直到任务结束。
运行结果
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
鸿篇巨制
心血之作
共勉! 鸿蒙必定成功,也必然成功.
http://weharmony.gitee.io/history.html 百篇博客.定期更新