4. 优先级天花板协议:协议原理、与优先级继承的区别、代码示例(VxWorks优先级天花板)、适用场景
好,咱们接着聊优先级反转的解决方案。
上一节我们讲了优先级继承,那是个很实用的机制。但说实话,我在实际项目中用得更顺手的,其实是优先级天花板协议。为什么?因为它更简单、更粗暴、也更可靠。
4.1 协议原理:一句话说清楚
优先级天花板协议的核心思想,说白了就是一句话:
谁拿了锁,谁就立刻把优先级提到“天花板”那么高。
这个“天花板”,就是所有可能竞争这把锁的任务中,最高的那个优先级。
举个例子。假设有三个任务:T1(优先级10)、T2(优先级20)、T3(优先级30)。它们都可能访问同一个共享资源。那么这把锁的“天花板优先级”就是30。
只要T1拿到了锁,它的优先级立刻被提升到30。这时候T2就算想抢跑,也抢不过T1。T1能一口气把资源用完、释放锁,然后降回原来的优先级。
你想想看,这比优先级继承简单多了吧?不需要去查“谁在等我”、“谁阻塞了谁”,直接一刀切——锁的优先级就是天花板。
4.2 与优先级继承的区别
很多初学者容易把这两个搞混。我当年也踩过这个坑。咱们用一张表说清楚:
| 对比项 | 优先级继承 | 优先级天花板 |
|---|---|---|
| 提升时机 | 只有高优先级任务被阻塞时才提升 | 拿到锁就立刻提升 |
| 提升到什么优先级 | 阻塞它的那个任务的优先级 | 锁的“天花板”优先级(固定值) |
| 实现复杂度 | 较高,需要跟踪阻塞链 | 较低,只需查表 |
| 死锁风险 | 可能发生死锁(嵌套锁场景) | 可避免死锁(如果天花板设置正确) |
| 实时性 | 相对较好,只提升必要的任务 | 可能过度提升,造成优先级“膨胀” |
嗯,这里要注意一个关键点:优先级继承是被动的,优先级天花板是主动的。
优先级继承是“出事了再补救”——高优先级任务被堵住了,才去拉低优先级任务一把。而优先级天花板是“先预防”——不管有没有人堵,拿了锁就提优先级。
我个人习惯,在系统比较简单、锁的竞争不太激烈时,用优先级天花板。因为代码写起来清爽,调试也方便。
4.3 代码示例:VxWorks 中的优先级天花板
VxWorks 对优先级天花板的支持很直接。它通过信号量的属性来设置。
我给大家看一段我实际项目里用过的代码:
/* 定义天花板优先级 */
#define RESOURCE_CEILING_PRIO 50
/* 创建带优先级天花板的信号量 */
SEM_ID semResource;
semResource = semMCreate(SEM_Q_PRIORITY | SEM_INVERSION_SAFE |
SEM_CEILING_PRIO(RESOURCE_CEILING_PRIO));
/* 任务A:低优先级,拿锁干活 */
void taskA(void) {
while (1) {
semTake(semResource, WAIT_FOREVER);
/* 临界区:操作共享资源 */
printf("TaskA: 拿到锁,当前优先级=%d\n", taskPriorityGet(0));
taskDelay(10); /* 模拟干活 */
semGive(semResource);
taskDelay(100);
}
}
/* 任务B:中优先级,不碰锁 */
void taskB(void) {
while (1) {
/* 纯计算任务 */
printf("TaskB: 运行中,优先级=%d\n", taskPriorityGet(0));
taskDelay(5);
}
}
/* 任务C:高优先级,也要拿锁 */
void taskC(void) {
while (1) {
semTake(semResource, WAIT_FOREVER);
/* 临界区 */
printf("TaskC: 拿到锁,当前优先级=%d\n", taskPriorityGet(0));
taskDelay(5);
semGive(semResource);
taskDelay(50);
}
}
小提示:SEM_INVERSION_SAFE 这个标志就是告诉 VxWorks 启用优先级天花板机制。如果你忘了加这个标志,那信号量就只是普通的互斥信号量,优先级反转该发生还是会发生。
这段代码跑起来后,你会发现一个有意思的现象:TaskA 拿锁期间,它的优先级会跳到50。这时候 TaskB 虽然优先级比 TaskA 原来的优先级高,但比天花板低,所以 TaskB 抢不过 TaskA。TaskA 能安心干完活再释放锁。
4.4 适用场景
优先级天花板不是万能的。我给大家总结一下它的“用武之地”:
- 锁的竞争任务数量已知且固定:天花板优先级可以静态计算出来,不需要动态调整。
- 系统对实时性要求高,但实现要简单:比如一些工业控制器、简单的嵌入式系统。
- 嵌套锁场景较少:如果锁嵌套很深,天花板协议可能导致优先级过度提升,反而影响实时性。
- 你不想处理复杂的阻塞链追踪:嗯,说白了就是懒人福音。我承认,有时候我就是图省事。
警告:天花板优先级设置过高,会导致“优先级膨胀”。比如一个低优先级任务拿了锁,被提到很高的优先级,它可能会长时间霸占CPU,影响其他不相关的任务。我曾经在一个项目中,把天花板设成了系统最高优先级,结果一个打印日志的低优先级任务,把整个系统的实时性拖垮了。切记:天花板不是越高越好,够用就行。
4.5 知识体系图
下面这张图,是我自己画的一个总结。它把优先级天花板协议的核心逻辑串起来了:
这张图里,任务A拿了锁之后,优先级直接跳到天花板50。任务B虽然优先级20比A原来的10高,但比天花板低,所以抢不了。任务C优先级30,同样被压制。直到A释放锁,一切恢复正常。
嗯,这就是优先级天花板协议的精髓。简单、粗暴、有效。下一节我们聊聊另一种更优雅的方案——优先级置顶协议。不过那是后话了。
公众号:蓝海资料掘金营,微信deep3321