基本概念
信号量(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被唤醒,执行删除信号量,删除两个任务。
实例运行结果:
经常有很多小伙伴抱怨说:不知道学习鸿蒙开发哪些技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?
为了能够帮助到大家能够有规划的学习,这里特别整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页)
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
开发基础知识:
1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……
基于ArkTS 开发
1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……
鸿蒙开发面试真题(含参考答案)
OpenHarmony 开发环境搭建
《OpenHarmony源码解析》
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……
OpenHarmony 设备开发学习手册
写在最后
如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
- 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
- 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。