优先级反转:
-
低优先级任务 C 首先持锁;
-
高优先级任务 A 之后也试图持锁,但是锁被 C 占用,所以 A 持锁失败;
-
在 C 执行过程中,中等优先级任务 B 被唤醒,抢占了任务 C 的执行;
此后如果任务B一直无耻的占用CPU,则C就一直得不到运行,C得不到运行就无法释放锁导致最高优先级任务A也无法被运行。
通过优先级继承,当任务 A 持锁失败时,锁的 owner task(任务 C)临时的把优先级上升至和任务 A 一样的优先级,在释放锁时,将优先级进行恢复。
实现逻辑:
要实现优先级继承,内核需要维护一条 PI Chain,保存进程持有锁和等待锁的依赖关系。PI Chain 是一个树形结构:
-
一个 task 可能持有 N 个 rt_mutex;
-
每个 rt_mutex 只能被一个 task 持有;
-
一个 rt_mutex 可能让 M 个 task 挂起;
-
每个 task 只会挂起在一个 rt_mutex 上;
-
task 通过 pi_waiter 可以遍历到它持有的每个 rt_mutex 上挂起的最高优先级的 task(top_waiter)。
一个复杂的场景如下:
根据上述场景,则 PI Chain 如下:
上述例子中一共有 5 个 PI Chain:
-
Task D->Lock 2->Task B->Lock 1->Task A;
-
Task E->Lock 2->Task B->Lock 1->Task A;
-
Task F->Lock 3->Task B->Lock 1->Task A;
-
Task G->Lock 3->Task B->Lock 1->Task A;
-
Task C->Lock 1->Task A;
为了维护优先级关系,处于 PI Chain 中右端任务的优先级必须大于等于 PI Chain 中左端的任务们;
比如上述例子中:Task A 的优先级必须大于 Task B/C/D/E/F/G,Task B 的任务优先级必须大于 Task D/E/F/G;
上述例子使用链表实现的,目前的Linux代码中采用红黑树的方式组织rt_mutex_waiter,实现逻辑是一样的: