19.1 互斥量基本概念
互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量,它和信号量不同的是,它支持互斥量所有权、递归访问以及防止优先级翻转的特性,用于实现对临界资源的独占式处理。
意时刻互斥量的状态只有两种,开锁或闭锁。
当互斥量被任务持有时,该互斥量处于闭锁状态,这个任务获得互斥量的所有权。当该任务释放这个互斥量时,该互斥量处于开锁状态, 任务失去该互斥量的所有权
当一个任务持有互斥量时,其他任务将不能再对该互斥量进行开锁或持有。 持有该互斥量的任务也能够再次获得这个锁而不被挂起,这就是递归访问,也就是递归互斥量的特性,这个特性与一般的信号量有很大的不同,在信号量中,由于已经不存在可用的信号量, 任务递归获取信号量时会发生主动挂起任务最终形成死锁。
如果想要用于实现同步(任务之间或者任务与中断之间),二值信号量或许是更好的选择,虽然互斥量也可以用于任务与任务、 任务与中断的同步,但是互斥量更多的是用于保护资源的互锁。
互锁的互斥量可以充当保护资源的令牌, 当一个任务希望访问某个资源时,它必须先获取令牌。当任务使用完资源后,必须还回令牌,以便其它任务可以访问该资源。
信号量会导致的另一个潜在问题,那就是任务优先级翻转,FreeRTOS 提供的互斥量可以通过优先级继承算法, 可以降低优先级翻转问题产生的影响,所以,用于临界资源的保护一般建议使用互斥量。
19.2 互斥量的优先级继承机制(防止优先级反转)
注:互斥量和递归信号量都有优先级继承机制
在 FreeRTOS 操作系统中为了降低优先级翻转问题利用了优先级继承算法。
优先级继承算法:
暂时提高某个占有某种资源的低优先级任务的优先级,使之与在所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低优先级任务执行完毕释放该资源时,优先级重新回到初始设定值。
互斥量与二值信号量最大的不同:
- 互斥量具有优先级继承机制,而信号量没有:某个临界资源受到一个互斥量保护,如果这个资源正在被一低优先级任务使用,那么此时的互斥量是闭锁状态,也代表了没有任务能申请到这个互斥量,如果此时一个高优先级任务想要对这个资源进行访问,去申请这个互斥量,那么高优先级任务会因为申请不到互斥量而进入阻塞态,那么系统会将现在持有该互斥量的任务的优先级临时提升到与高优先级任务的优先级相同,这个优先级提升的过程叫做优先级继承。 防止优先级发生反转。
优先级反转:
任务的优先级在创建的时候就已经是设置好的,高优先级的任务可以打断低优先级的任务,抢占 CPU 的使用权。但是在很多场合中,某些资源只有一个,当低优先级任务正在占用该资源的时候,即便高优先级任务也只能乖乖的等待低优先级任务使用完该资源后释放资源。这里高优先级任务无法运行而低优先级任务可以运行的现象称为“优先级翻转” 。
危害:
因为在我们一开始创造这个系统的时候,我们就已经设置好了任务的优先级了,越重要的任务优先级越高。但是发生优先级翻转,对我们操作系统是致命的危害,会导致系统的高优先级任务阻塞时间过长。
- L 任务正在使用某临界资源, H 任务被唤醒,执行 H 任务。但 L 任务并未执行完毕,此时临界资源还未释放
- 这个时刻 H 任务也要对该临界资源进行访问,但 L 任务还未释放资源,由于保护机制, H 任务进入阻塞态, L 任务得以继续运行,此时已经发生了优先级翻转现象。
- 某个时刻 M 任务被唤醒,由于 M 任务的优先级高于 L 任务, M 任务抢占了 CPU 的使用权, M 任务开始运行,此时 L 任务尚未执行完,临界资源还没被释放,
- M 任务运行结束,归还 CPU 使用权, L 任务继续运行。
- L任务运行结束,释放临界资源, H 任务得以对资源进行访问, H 任务开始运行
在这过程中, H 任务的等待时间过长,这对系统来说这是很致命的,所以这种情况不允许出现,而互斥量就是用来降低优先级翻转的产生的危害。
优先级继承:
- L 任务正在使用某临界资源, L 任务正在使用某临界资源, H 任务被唤醒,执行 H 任务。但 L 任务并未执行完毕,此时临界资源还未释放
- 某一时刻 H 任务也要对该资源进行访问,由于保护机制, H 任务进入阻塞态。此时发生优先级继承,系统将 L 任务的优先级暂时提升到与 H 任务优先级相同, L任务继续执行。
- 在某一时刻 M 任务被唤醒,由于此时 M 任务的优先级暂时低于 L 任务,所以 M 任务仅在就绪态,而无法获得 CPU 使用权
- L任务运行完毕, H 任务获得对资源的访问权, H 任务从阻塞态变成运行态,此时 L 任务的优先级会变回原来的优先级。
- 当 H 任务运行完毕, M 任务得到 CPU 使用权,开始执行
- 系统正常运行,按照设定好的优先级运行。
注意:硬实时系统在一开始设计时就要避免优先级反转发生
19.3 互斥量应用场景
互斥量更适合于:
可能会引起优先级翻转的情况。
递归互斥量:
任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造成死锁的问题。
多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥量可被用于对临界资源的保护从而实现独占式访问。另外,互斥量可以降低信号量存在的优先级翻转问题带来的影响。
两个任务需要对串口进行发送数据,其硬件资源只有一个,那么两个任务肯定不能同时发送啦,不然导致数据错误,那么,就可以用互斥量对串口资源进行保护,当一个任务正在使用串口的时候,另一个任务则无法使用串口,等到任务使用串口完毕之后,另外一个任务才能获得串口的使用权。
注意:不能在中断中使用
另外需要注意的是互斥量不能在中断服务函数中使用,因为其特有的优先级继承机制只在任务起作用,在中断的上下文环境毫无意义
19.4 互斥量运作机制
多任务环境下会存在多个任务访问同一临界资源的场景,该资源会被任务独占处理。其他任务在资源被占用的情况下不允许对该临界资源进行访问,这个时候就需要用到FreeRTOS 的互斥量来进行资源保护。
- 因为互斥量具有优先级继承机制,一般选择使用互斥量对资源进行保护,如果资源被占用的时候,无论是什么优先级的任务想要使用该资源都会被阻塞.
- 假如正在使用该资源的任务 1 比阻塞中的任务 2 的优先级还低,那么任务1 将被系统临时提升到与高优先级任务 2 相等的优先级(任务 1 的优先级从 L变成 H)
- 当任务 1 使用完资源之后,释放互斥量,此时任务 1 的优先级会从 H 变回原来的 L
- 任务 2 此时可以获得互斥量,然后进行资源的访问,当任务 2 访问了资源的时候,该互斥量的状态又为闭锁状态,其他任务无法获取互斥量。