15.2-uC/OS-III资源管理(信号量)

时间:2023-03-08 17:57:32

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.优先级反转

优先级反转是实时系统中的一个常见问题,仅存在于基于优先级
的抢占式内核中。

15.2-uC/OS-III资源管理(信号量)

( 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,用于解决
优先级反转问题。

15.2-uC/OS-III资源管理(信号量)

( 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时允许设置期限, 这样能防止死锁, 但是同样
的死锁可能稍后再次出现。