优先级反转(Priority Inversion)
指某同步资源被较低优先级的进程/线程所拥有,较高优先级的进程/线程竞争该同步资源时未获得该资源,而使得较高优先级进程/线程反而推迟被调度执行的现象。
对基于优先级调度的实时系统,优先级高的进程/线程被优先调度是调度算法首要考虑的因素。
假定一个进程中有三个线程Thread1、Thread2和Thread3,它们的优先级顺序是Priority(Thread1) > Priority(Thread2) > Priority(Thread3)。
图1 优先级反转
- T0时刻只有Thread3处于可运行状态,运行过程中,Thread3拥有了一个同步资源SYNCH1;
- T1时刻,Thread2就绪进入可运行状态,由于优先级高于正在运行的Thread3,Thread3被抢占(未释放同步资源SYNCH1),Thread2被调度执行;
- 同样的,T2时刻,Thread1抢占Thread2;
- Thread1运行到T3时刻,需要同步资源SYNCH1,但SYNCH1被更低优先级的Thread3所拥有,Thread1被挂起等待该资源,而此时处于可运行状态的线程Thread2和Thread3中,Thread2的优先级大于Thread3的优先级,Thread2被调度执行。
上述现象中,优先级最高的Thread1既要等优先级低的Thread2运行完,还要等优先级更低的Thread3运行完之后才能被调度,如果Thread2和Thread3执行的很费时的操作,显然Thread1的被调度时机就不能保证,整个实时调度的性能就很差了。
优先级继承(Priority Inheritance)
优先级继承也就是,高优先级进程TH在等待低优先级的线程TL继承占用的竞争资源时,为了使TH能够尽快获得调度运行,由操作系统把TL的优先级提高到TH的优先级,从而让TL以TH的优先级参与调度,尽快让TL执行并释放TH欲获得的竞争资源,然后TL的优先级调整到继承前的水平,此时TH可获得竞争资源而继续执行。
有了优先级继承,上述优先级反转的执行情况是:
图2 优先级继承
与图一比较,图二中,到了T3时刻,Thread1需要Thread3占用的同步资源SYNCH1,操作系统检测到这种情况后,就把Thread3的优先级提高到Thread1的优先级。此时处于可运行状态的线程Thread2和Thread3中,Thread3的优先级大于Thread2的优先级,Thread3被调度执行。
Thread3执行到T4时刻,释放了同步资源SYNCH1,操作系统这时恢复了Thread3的优先级,Thread1获得了同步资源SYNCH1,重新进入可执行队列。处于可运行状态的线程Thread1和Thread2中,Thread1的优先级大于Thread2的优先级,所以Thread1被调度执行。
上述机制,使优先级最高的Thread1获得执行的时机提前。
小趣闻:
这里还有一个八卦,1997年的美国的火星探测器(使用的就是vxworks)就遇到一个优先级反转问题引起的故障。简单说下,火星探测器有一个信息总线,有一个高优先级的总线任务负责总线数据的存取,访问总线都需要通过一个互斥锁(共享资源出现了);还有一个低优先级的,运行不是很频繁的气象搜集任务,它需要对总线写数据,也就同样需要访问互斥锁;最后还有一个中优先级的通信任务,它的运行时间比较长。平常这个系统运行毫无问题,但是有一天,在气象任务获得互斥锁往总线写数据的时候,一个中断发生导致通信任务被调度就绪,通信任务抢占了低优先级的气象任务,而无巧不成书的是,此时高优先级的总线任务正在等待气象任务写完数据归还互斥锁,但是由于通信任务抢占了CPU并且运行时间比较长,导致气象任务得不到CPU时间也无法释放互斥锁,本来是高优先级的总线任务也无法执行,总线任务无法及时执行的后果被探路者认为是一个严重错误,最后就是整个系统被重启。Vxworks允许优先级继承,然而遗憾的工程师们将这个选项关闭了。
参考资料: