乐趣区

关于操作系统:讨论关于v404版本中解决的使用互斥量导致优先级反转的问题探讨

本文由 RT-Thread 论坛用户 @杰阿阿杰原创公布:https://club.rt-thread.org/as…
对于 RT-Thread v4.0.4 版本中解决的应用互斥量导致优先级反转的问题探讨

 昨天晚上(2021.10.20),rtt 组织了一场线上发布会,展现了 v4.0.4 版本的一些新个性,以及修复的一些问题。其中,@满鉴霆 老师演讲中讲述的一个对于应用互斥量导致线程优先级反转问题,很有意思。

一、简略介绍互斥量

​ 互斥量是线程间同步的一种形式,又叫互相排挤的信号量,是一种非凡的二值信号量。互斥量相似于只有一个车位的停车场:当有一辆车进入的时候,将停车场大门锁住,其余车辆在里面等待。当外面的车进去时,将停车场大门关上,下一辆车才能够进入。(援用自 RTT 文档)
二、互斥量解决了什么问题
2.1 线程优先级反转问题

​ 假如以后有三条线程,别离是 A、B、C,它们的优先级关系是 A > B > C,以及一个专用的内存空间 M。为了保障内存空间内数据的安全性,同一时间段内不能有超过一条线程进行操作。即当 C 正在读取 M 的数据时,A 或 B 不能对 M 做批改。

​ 因为这样的规定,会造成优先级反转问题:

61fa16d75610ca418c881a704571fa29.jpg.webp

C 就绪,并取得了 M 的控制权
A 就绪,优先级比 C 高,CPU 优先解决 A
A 尝试获取 M 的控制权,因为 C 正在持有 M 的控制权,因而挂起期待;C 持续读取 M
B 就绪,优先级比 C 高,CPU 优先解决 B
B 工作执行实现并挂起,C 持续读取 M
C 实现了读取 M 数据的操作,开释了 M 的控制权,轮到 A 对 M 进行批改

​ 通过下面的流程,很显著,咱们发现,尽管线程 B 的优先级比线程 A 低,然而却优先执行了,这不合乎咱们对系统实时性的要求。
2.2 互斥量的解决办法

​ 互斥量应用优先级继承协定,解决了上述的优先级反转问题:

ad862ca851af7f05406b3d88d230b11a.jpg.webp

C 就绪,并取得了 M 的控制权
A 就绪,优先级比 C 高,CPU 优先解决 A
A 尝试获取 M 的控制权,因为 C 正在持有 M 的控制权,因而挂起期待;根据优先级继承协定,线程 C 的优先级被晋升到与 A 相等,即此时线程优先级关系是:A = C > B;C 持续读取 M
C 实现了读取 M 数据的操作,开释了 M 的控制权,优先级被复原原样,轮到 A 对 M 进行批改,唤醒 A
A 工作执行实现并挂起;B 在 3 - 4 之间已就绪,过后因优先级比 C 低,所以无奈失去执行,而此时优先级比 C 高,CPU 优先唤醒解决 B
B 工作执行实现并挂起,C 持续实现工作

三、互斥量制作了什么问题
3.1 谬误地应用了 FIFO flag

​ 当用户须要防止上述线程优先级反转问题时,就须要用到互斥量对线程做同步。互斥量由 IPC 容器治理,因而线程想要获取互斥量时,须要在 IPC 中排队期待。IPC 的排队形式有两种:

RT_IPC_FLAG_FIFO:先进先出,队列依照先进先出形式排队
RT_IPC_FLAG_PRIO:优先级期待,队列将依照优先级进行排队,优先级高的期待线程将会插队排在优先级低的期待线程前

​ FIFO 属于非实时调度形式,所有排队期待的线程不再具备优先级的个性。然而,在创立 / 初始化(create/init)互斥量时,函数却容许用户应用 RT_IPC_FLAG_FIFO 参数,这会导致如下情景:

2832d07e4bdede3e851bb696ecd73a44.jpg.webp

C 就绪,并取得了 M 的控制权
B 就绪,优先级比 C 高,CPU 优先解决 B
B 尝试获取 M 的控制权,因为 C 正在持有 M 的控制权,因而挂起期待;根据优先级继承协定,线程 C 的优先级被晋升到与 B 相等,即此时线程优先级关系是:A > B = C,B 进入 FIFO 队列,并排在第一位;C 持续读取 M
A 就绪,优先级比 C 高,CPU 优先解决 A
A 尝试获取 M 的控制权,因为 C 正在持有 M 的控制权,因而挂起期待;根据优先级继承协定,线程 C 的优先级被晋升到与 A 相等,即此时线程优先级关系是:A = C > B,A 进入 FIFO 队列,依据先进先出准则,排在第二位,B 前面;C 持续读取 M
C 实现了读取 M 数据的操作,开释了 M 的控制权,优先级被复原原样,依据 FIFO 队列,轮到 B 持有 M 的控制权,唤醒 B
B 实现了读取 M 数据的操作,开释了 M 的控制权,优先级被复原原样,依据 FIFO 队列,轮到 A 持有 M 的控制权,唤醒 A
A 工作执行实现并挂起,B 持续工作
B 工作执行实现并挂起,C 持续工作

​ 由此咱们发现,尽管 A 优先级比 B 高,然而因为 B 比 A 先进入 FIFO 队列,导致 B 比 A 优先失去 M 的控制权,并优先执行,这不合乎咱们应用互斥量的目标。

​ 同时,因为 A 挂起期待互斥量,因而 B 开释互斥量之前,A 都不会被唤醒(除非超时)。这会使得其余优先级高于 B,低于 A 的线程都会优先于 A 执行。谁能忍?
3.2 正确的应用形式

​ 新版本已修复以上呈现的优先级反转问题,在创立 / 初始化(create/init)互斥量时,疏忽用户给出的排队形式(flag),只应用 RT_IPC_FLAG_PRIO。

c5a35e5cef2d41d0e65ff629fc33f257.jpg.webp
四、总结

​ 互斥量的诞生就是为了解决优先级反转的问题,然而谬误地应用互斥量反而会让状况变得更蹩脚。同时,这个 bug 绝对荫蔽,不易被觉察,初学者(比方我)容易谬误地应用,调试时也不容易复现。因而,修复此 bug 是很重要的。
五、结尾

​ 感激 RTT 昨晚组织的个性解读会,的抽奖流动,让我终于中了一次奖哈哈哈!尽管是三等奖一条数据线,但这是我用这个抽奖小程序以来第一次中奖,真的很想吐槽那个抽奖小程序……

​ 另外,解读会有回放(尽管我临时不晓得在哪)。

退出移动版