作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习
擅长领域:驱动开发,嵌入式软件开发,BSP开发
作者主页:一个平凡而乐于分享的小比特的个人主页
文章收录专栏:UCOS-III,本专栏为UCOS-III学习记录
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
UCOS-III笔记(七)
信号量
一种解决同步问题的机制,可以实现对共享资源的有序访问
特点:
- 当计数值大于0,代表有信号量资源
- 当释放信号量,信号量计数值(资源数)加一
- 当获取信号量,信号量计数值(资源数)减一
- 信号量:用于传递状态
- 当信号量如果最大值被限定为1,那么它就是二值信号量;如果最大值不是1,它就是计数型信号量
二值信号量
二值信号量实际上就只有空和满两种情况,所以称为二值。
二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!

使用二值信号量的过程:创建二值信号量 -> 释放二值信号量 -> 获取二值信号量
| 函数 | 描述 |
|---|---|
| OSSemCreate() | 创建一个信号量 |
| OSSemDel() | 删除一个信号量 |
| OSSemPend() | 获取信号量资源 |
| OSSemPendAbort() | 终止任务挂起等待信号量资源 |
| OSSemPost() | 释放信号量资源 |
| OSSemSet() | 强制设置信号量的资源数 |
OSSemCreate():创建一个信号量
c
void OSSemCreate( OS_SEM * p_sem, //指向信号量结构体的指针
CPU_CHAR * p_name, //指向作为信号量名的 ASCII 字符串的指针
OS_SEM_CTR cnt, //信号量资源数的初始值
OS_ERR * p_err //指向接收错误代码变量的指针
)
OSSemPost():释放信号量
c
OS_SEM_CTR OSSemPost( OS_SEM * p_sem, //指向信号量结构体的指针
OS_OPT opt, //函数操作选项:
//OS_OPT_POST_1:只给一个任务(最高优先级的)发信号 //OS_OPT_POST_ALL:给所有等待该信号量的任务发信号
//以下宏可以与上面两个搭配使用
//OS_OPT_POST_NO_SCHED :禁止在本函数内执行任务调度
OS_ERR * p_err //指向接收错误代码变量的指针
)
OS_SEM_CTR 类型返回值为信号量资源数更新后的值
OSSemPend():获取信号量
c
OS_SEM_CTR OSSemPend( OS_SEM * p_sem, //指向信号量结构体的指针
OS_TICK timeout, //任务挂起等待信号量的最大允许时间,当为0时,表示将一直等待,直到接收到信号
OS_OPT opt, //OS_OPT_PEND_BLOCKING :如果信号量没有资源的话就阻塞任务 //OS_OPT_PEND_NON_BLOCKING :如果信号量没有资源任务就直接返回
CPU_TS * p_ts, //指向接收信号量接收时的时间戳的变量的指针
OS_ERR * p_err //指向接收错误代码变量的指针
)
OS_SEM_CTR 类型返回值为信号量资源数更新后的值
计数型信号量
计数型信号量与二值信号量是很相似的 ,但是计数型信号量的资源数大于1,也就是它的资源不止0和1
计数型信号量适用场合:
-
事件计数
当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0
-
资源管理
信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目
注意:计数型信号量和二值信号量的API函数是公用的
优先级翻转
- 优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行
- 优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果
- 在使用二值信号量的时候,经常会遇到优先级翻转的问题
举个例子:

高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)
互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

此时任务H的阻塞时间仅仅是任务L的执行时间,将优先级翻转的危害降到了最低,优先级继承并不能完全的消除优先级翻转的问题
注意:互斥信号量不能用于中断服务函数中,原因如下:
- 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用于任务中,不能用于中断服务函数
- 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态(中断要快进快出)
使用流程:创建互斥信号量 -> 获取信号量 -> 释放信号量
| 函数 | 描述 |
|---|---|
| OSMutexCreate() | 创建一个互斥信号量 |
| OSMutexDel() | 删除一个互斥信号量 |
| OSMutexPend() | 获取互斥信号量 |
| OSMutexPost() | 释放互斥信号量 |
注意:创建互斥信号量时,默认信号量有效
OSMutexCreate():创建一个互斥信号量
c
void OSMutexCreate ( OS_MUTEX* p_mutex, //指向互斥信号量结构体的指针
CPU_CHAR * p_name, //指向作为信号量名的 ASCII 字符串的指针
OS_ERR * p_err //指向接收错误代码变量的指针
)
OSMutexPend():获取互斥信号量
c
void OSMutexPend ( OS_MUTEX* p_mutex, //指向互斥信号量结构体的指针
OS_TICK timeout, //任务挂起等待信号量的最大允许时间
OS_OPT opt, //OS_OPT_PEND_BLOCKING :如果信号量没有资源的话就阻塞任务 //OS_OPT_PEND_NON_BLOCKING :如果信号量没有资源任务就直接返回
CPU_TS * p_ts, //指向接收互斥信号量接收时的时间戳的变量的指针
OS_ERR * p_err //指向接收错误代码变量的指针
)
OSMutexPost():释放互斥信号量
c
Void OSMutexPost ( OS_MUTEX* p_mutex, //指向互斥信号量结构体的指针
OS_OPT opt, //OS_OPT_POST_NONE:不指定特定的选项
//OS_OPT_POST_NO_SCHED :禁止在本函数内执行任务调度
OS_ERR * p_err //指向接收错误代码变量的指针
)