优先级倒置是一个常见且有些古老的问题。处理过 OS 进程调度的人,特别是有实时性要求的人,对它很熟悉。这个问题几乎没有众所周知的解决方案,每个都有其优点和缺点:
- 禁用所有中断以保护关键部分
- 优先上限
- 优先继承
- 随机提升
选择哪种方法来处理优先级倒置并不重要;鉴于应用程序使用定义良好的接口来同步共享资源,所有这些都相对容易在 OS 内核中实现。例如,如果一个进程使用,例如,锁定一个互斥锁,pthread_mutex_lock
操作系统很清楚这一事实,因为这个函数在深处执行系统调用(即futex
在 Linux 上)。当内核处理这个请求时,它对谁在等待什么有一个完整而清晰的画面,并且可以决定如何最好地处理优先级反转。
现在,假设内核不知道进程何时锁定/解锁互斥锁。例如,如果使用原子 CPU 指令来实现互斥锁(如“无锁”算法),就会发生这种情况。然后,低优先级的进程可能会因为更高优先级的任务而获得锁并暂停执行。然后,当安排更高优先级的任务时,它会简单地烧毁 CPU,试图锁定“自旋锁”。像这样的死锁会使整个系统变得毫无用处。
鉴于上述情况以及我们无法更改程序以不使用原子操作来同步对共享资源的访问这一事实,问题归结为检测代码何时尝试这样做。
我有一些有点模糊的启发式想法,这些想法既难以实施,又可能会产生误报。他们来了:
- 不时查看程序计数器寄存器并尝试检测代码只是在紧密循环中烧毁 CPU。如果代码在该位置被发现 N 次,则暂停该进程并让其他优先级较低的进程有机会运行并解锁互斥锁。这种方法离理想太远了,可能会产生太多误报。
- 对进程可以运行的时间有硬性限制。这会立即降低调度程序的硬实时功能,但它可以工作。然而,问题在于,在“死锁”情况下,高优先级进程将浪费其所有时间窗口来尝试获取繁忙的资源。
- 我不知道这是否可能,但另一个想法是拦截/插入原子 CPU 指令,让调度程序知道锁定/解锁尝试。换句话说,本质上是将原子 CPU 操作转换为某种系统调用。当 MMU 发出页面错误信号时,它的机制与创建虚拟页面映射的方式有些接近。
你怎么看上面的想法?您还能想到哪些其他检测此类代码的方法?