15.2-uC/OS-III资源管理(信号量)
1.信号量
信号量是一个“ 锁定机构”,代码需要获得钥匙才可以访问共享资
源。占用该资源的任务不再使用该资源并释放资源时,其它任务才能
够访问这个资源。
通常有两种类型的信号量:二值信号量和多值信号量。
(1).二值信号量
二值信号量的值只能是 0或 1.
(2).多值信号量
多值信号量计数值可以是 0到 4294967295(依赖于计数值是 8位, 16位或 32位)。
特别的, uC/OS-III中的信号量计数值最大为OS_SEM_CTR(见OS_TYPE.H)。
根据信号量计数值,uC/OS-III可以知道有该信号量可以再被多少个任务获得。
只有任务才允许使用信号量, ISR是不允许的。
2.信号量相关的函数
函数名 | 功能 |
OSSemCreate() | 创建一个信号量 |
OSSemDel() | 删除一个信号量 |
OSSemPend() | 等待某个信号量 |
OSSemPendAbort() | 取消等待某个信号量 |
OSSemPost() | 释放或标记信号量 |
OSSemSet() | 设置信号量计数值 |
3信号量需注意:
(1).用信号量访问共享资源不会导致中断延迟。当任务在执行信号量
所保护的共享资源时, ISR或高优先级任务可以抢占该任务。
(2).应用中可以有任意个信号量用于保护共享资源。然而,推荐将信
号量用于I/O端口的保护,而不是内存地址。
(3).信号量经常被过度使用。很多情况下,访问一个简短的共享资源
时不推荐使用信号量, 请求和释放信号量会消耗CPU时间。 通过关/
开中断能更有效地执行这些操作。
信号量会导致一种严重的问题:优先级反转。
4.优先级反转
优先级反转是实时系统中的一个常见问题,仅存在于基于优先级
的抢占式内核中。
( 1) 任务H( High优先级) 和任务M( Medium优先级) 都在
等待事件发生,任务L( Low优先级)正在被运行。
( 2)这时,任务L想申请信号量。
( 3)任务L访问共享资源。
( 4)任务H所等待的事件发生, uC/OS-III挂起任务L。
( 5)开始执行任务H。
( 6)现在任务H想要访问任务L所占用的共享资源(此时该
共享资源被任务L占用)。因为该资源被任务L占用,任务H 被放
入挂起队列中等待这个信号量被释放。
( 7)任务L被恢复并继续执行。
( 8)任务L被任务M抢占因为任务M所等待的事件发生。
( 9)任务M开始执行。
( 10)任务M执行完毕, uC/OS-III将CPU控制权交还给任务L。
( 11)任务L被执行。
( 12)任务L执行完毕并释放资源。 此时, 任务H获得该资源,
uC/OS-III上下文切换到任务H。
( 13)任务H开始执行。
可以看出,任务H在任务L后被执行,因为任务H等待任务L
所占用的资源。麻烦在于任务M抢占任务L,若任务M需要执行很
长时间,则任务H会被延迟很长时间才执行,这叫做优先级反转。
解决优先级反转:
可以通过提升任务L的优先级解决这种问题(只在该任务访问共
享资源时),访问结束后就恢复任务的优先级。任务L的优先级需要
被上升到任务H的优先级。
5.互斥信号量mutex
uC/OS-III支持一种特殊类型的二值信号量叫做mutex,用于解决
优先级反转问题。
( 1)任务H和任务M等待事件的发生,任务L被执行。
( 2)此时,任务L申请并获得一个 mutex。
( 3)任务L被执行。
( 4)任务H和任务M所等待的事件发生,任务L被抢占。
( 5)任务H开始执行。
( 6)任务H想要访问任务L占用的共享资源。考虑到任务L
占用这个资源, uC/OS-III提升任务L的优先级与任务H相同。这样
就防止了任务L被任务M抢占(被中等优先级的任务抢占)。
( 7)任务L继续访问这个资源, 但现在任务L的优先级等于任务
H的优先级。注意的是任务H被挂起因为它要等待任务L释放
mutex。换句话说,任务H被插入到mutex的挂起队列中。
( 8)任务L对共享资源的访问执行完毕并释放mutex。uC/OS-III
恢复任务L到原有的优先级。然后uC/OS-III将这个mutex交给任务
H。 {并不是任务L已经全部执行完毕,而是当它释放信号量时就会
发生调度而转到高优先级任务}
( 9)任务H开始执行。
( 10)任务H对共享资源的访问完毕,并释放这个mutex。
( 11)任务H继续执行。
( 12)任务H执行完毕。 uC/OS-III将CPU控制权交给任务M。
( 13) 任务M被执行。
mutex是一个内核对象,它被数据类型OS_MUTEX所定义,
(OS_MUTEX的原型是os_mutex,见OS.H)。应用中可以有任意个
mutex(仅限于处理器的RAM)。
只有任务才可以使用mutex( ISR不可以使用mutex)。
uC/OS-III允许任务嵌套占有mutex。如果任务获得mutex,那么
它可以嵌套获得这个mutex多达250次。该任务需要释放相同次数才
能释放掉这个mutex。
6.与mutex相关的函数
函数名 | 功能 |
OSMutexCreate() | 创建一个mutex |
OSMutexDle() | 删除一个mutex |
OSMutexPend() | 等待一个mutex |
OSMutexPendAbort() | 任务取消等待mutex |
OSMutexPost() | 释放mutex |
任务占用共享资源前必须获得mutex。通过调用OSMutexPend()
申请这个信号量。
注意:(1).mutex是二值信号量,所以没必要初始化计数值。
(2).任何时候mutex只能提供给一个任务。 事实上, 推荐当你申请
一个mutex时,最好不要申请其它内核对象。
7.死锁
死锁,就是两个任务互相等待对方所占用的资源的情况
可以用以下方式防止死锁:
1) 同一个时间不要申请多于一个mutex
2) 不要直接地申请mutex( 该申请放到器件驱动中和可重入函数中)
3) 在处理之前先获得全部所需要的mutex
4) 任务间以同样的顺序申请资源
当申请信号量或mutex时允许设置期限, 这样能防止死锁, 但是同样
的死锁可能稍后再次出现。