v29.05 鸿蒙内核源码分析(信号量) | 谁在负责解决任务的同步 原创
子绝四:毋意、毋必、毋固、毋我。 《论语》:子罕篇
百篇博客系列篇.本篇为:
v29.xx 鸿蒙内核源码分析(信号量篇) | 谁在负责解决任务的同步
进程通讯相关篇为:
- v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志
- v27.05 鸿蒙内核源码分析(互斥锁) | 比自旋锁丰满的互斥锁
- v28.04 鸿蒙内核源码分析(进程通讯) | 九种进程间通讯方式速揽
- v29.05 鸿蒙内核源码分析(信号量) | 谁在负责解决任务的同步
- v30.07 鸿蒙内核源码分析(事件控制) | 任务间多对多的同步方案
- v33.03 鸿蒙内核源码分析(消息队列) | 进程间如何异步传递大数据
本篇说清楚信号量
读本篇之前建议先读鸿蒙内核源码分析(总目录)其他篇幅.
基本概念
信号量(Semaphore) 是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。
一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况:
0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。
正值,表示该信号量当前可被获取。
以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同:
用作互斥时,初始信号量计数值不为0,表示可用的共享资源个数。在需要使用共享资源前,先获取信号量,然后使用一个共享资源,使用完毕后释放信号量。这样在共享资源被取完,即信号量计数减至0时,其他需要获取信号量的任务将被阻塞,从而保证了共享资源的互斥访问。另外,当共享资源数为1时,建议使用二值信号量,一种类似于互斥锁的机制。
用作同步时,初始信号量计数值为0。任务1获取信号量而阻塞,直到任务2或者某中断释放信号量,任务1才得以进入Ready或Running态,从而达到了任务间的同步。
信号量运作原理
信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,通过 LOSCFG_BASE_IPC_SEM_LIMIT 宏实现),并把所有信号量初始化成未使用,加入到未使用链表中供系统使用。
- 信号量创建,从未使用的信号量链表中获取一个信号量,并设定初值。
- 信号量申请,若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,
等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。 - 信号量释放,若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。
- 信号量删除,将正在使用的信号量置为未使用信号量,并挂回到未使用链表。
信号量允许多个任务在同一时刻访问共享资源,但会限制同一时刻访问此资源的最大任务数目。
当访问资源的任务数达到该资源允许的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。
信号量长什么样?
semList
,这又是一个双向链表, 双向链表是内核最重要的结构体, 可前往 鸿蒙内核源码分析(总目录)
查看双向链表篇, LOS_DL_LIST
像狗皮膏药一样牢牢的寄生在宿主结构体上semList
上挂的是未来所有等待这个信号量的任务.
初始化信号量模块
分析如下:
- 初始化创建了信号量池来统一管理信号量, 默认 1024 个信号量
- 信号ID范围从 [0,1023]
- 未分配使用的信号量都挂到了全局变量
g_unusedSemList
上.
小建议:鸿蒙内核其他池(如进程池,任务池)都采用free
来命名空闲链表,而此处使用unused
,命名风格不太严谨,有待改善.
创建信号量
分析如下:
- 从未使用的空闲链表中拿首个信号量供分配使用.
- 信号量的最大数量和信号量个数都由参数指定.
- 信号量状态由
OS_SEM_UNUSED
变成了OS_SEM_USED
semHandle
带走信号量ID,外部由此知道成功创建了一个编号为*semHandle
的信号量
申请信号量
分析如下:
这个函数有点复杂,大量的goto
,但别被它绕晕了,盯着返回值看.
先说结果只有一种情况下申请信号量能成功(即 retErr == LOS_OK
)
其余申请失败的原因有:
- 信号量ID超出范围(默认1024)
- 中断发生期间
- 系统任务
- 信号量状态不对,信号量ID不匹配
以上都是异常的判断,再说正常情况下 semPended->semCount = 0
时的情况,没有资源了怎么办?
任务进入 OsTaskWait
睡眠状态,怎么睡,睡多久,由参数 timeout
定 timeout
值分以下三种模式:
无阻塞模式:即任务申请信号量时,入参
timeout
等于0。若当前信号量计数值不为0,则申请成功,否则立即返回申请失败。
永久阻塞模式:即任务申请信号量时,入参
timeout
等于0xFFFFFFFF。若当前信号量计数值不为0,则申请成功。
否则该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,直到有其他任务释放该信号量,阻塞任务才会重新得以执行。
定时阻塞模式:即任务申请信号量时,
0<timeout<0xFFFFFFFF
。若当前信号量计数值不为0,则申请成功。
否则,该任务进入阻塞态,系统切换到就绪任务中优先级最高者继续执行。任务进入阻塞态后,
超时前如果有其他任务释放该信号量,则该任务可成功获取信号量继续执行,若超时前未获取到信号量,接口将返回超时错误码。
在 OsTaskWait
中,任务将被挂入semList
链表,semList
上挂的都是等待这个信号量的任务.
释放信号量
分析如下:
- 注意看在什么情况下
semPosted->semCount
才会 ++ ,是在LOS_ListEmpty
为真的时候,semList
是等待这个信号量的任务.
semList
上的任务是在OsTaskWait
中挂入的.都在等这个信号. - 每次
OsSemPost
都会唤醒semList
链表上一个任务,直到semList
为空. - 掌握信号量的核心是理解
LOS_SemPend
和LOS_SemPost
编程示例
本实例实现如下功能:
-
测试任务Example_TaskEntry创建一个信号量,锁任务调度,创建两个任务Example_SemTask1、Example_SemTask2,Example_SemTask2优先级高于Example_SemTask1,两个任务中申请同一信号量,解锁任务调度后两任务阻塞,测试任务Example_TaskEntry释放信号量。
-
Example_SemTask2得到信号量,被调度,然后任务休眠20Tick,Example_SemTask2延迟,Example_SemTask1被唤醒。
-
Example_SemTask1定时阻塞模式申请信号量,等待时间为10Tick,因信号量仍被Example_SemTask2持有,Example_SemTask1挂起,10Tick后仍未得到信号量,
Example_SemTask1被唤醒,试图以永久阻塞模式申请信号量,Example_SemTask1挂起。 -
20Tick后Example_SemTask2唤醒, 释放信号量后,Example_SemTask1得到信号量被调度运行,最后释放信号量。
-
Example_SemTask1执行完,40Tick后任务Example_TaskEntry被唤醒,执行删除信号量,删除两个任务。
实例运行结果:
百万汉字注解.精读内核源码
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
基础工具>> 双向链表 | 位图管理 | 用栈方式 | 定时器 | 原子操作 | 时间管理 |
加载运行>> ELF格式 | ELF解析 | 静态链接 | 重定位 | 进程映像 |
进程管理>> 进程管理 | 进程概念 | Fork | 特殊进程 | 进程回收 | 信号生产 | 信号消费 | Shell编辑 | Shell解析 |
编译构建>> 编译环境 | 编译过程 | 环境脚本 | 构建工具 | gn应用 | 忍者ninja |
进程通讯>> 自旋锁 | 互斥锁 | 进程通讯 | 信号量 | 事件控制 | 消息队列 |
内存管理>> 内存分配 | 内存管理 | 内存汇编 | 内存映射 | 内存规则 | 物理内存 |
前因后果>> 总目录 | 调度故事 | 内存主奴 | 源码注释 | 源码结构 | 静态站点 |
任务管理>> 时钟任务 | 任务调度 | 任务管理 | 调度队列 | 调度机制 | 线程概念 | 并发并行 | CPU | 系统调用 | 任务切换 |
文件系统>> 文件概念 | 文件系统 | 索引节点 | 挂载目录 | 根文件系统 | 字符设备 | VFS | 文件句柄 | 管道文件 |
硬件架构>> 汇编基础 | 汇编传参 | 工作模式 | 寄存器 | 异常接管 | 汇编汇总 | 中断切换 | 中断概念 | 中断管理 |
鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。
http://weharmony.gitee.io/history.html 百篇博客.定期更新