这里介绍一种著名的调度算法,多级反馈队列(multi-level feedback queue,MLFQ),这种调度策略不但应用在Solaris和FreeBSD和Linux Schedule_RR Policy等Unix家族操作系统中,几乎所有的 RTOS操作系统使用的基于优先级的时间片轮转调度算法,也是MLFQ的一个精简化的变种.
多级反馈队列要解决两个方面的问题,首先,它要优化周转时间,这通过先执行短工作来实现,然而,操作系统通常不知道工作要运行多久,而这又是SJF(或者STCF)等算法所必须的,其次,MLFQ希望给交互用户(例如用户坐在屏幕前,等着进程响应)很好的交互体验,因此需要降低响应时间,然而,像轮转这样的算法虽然降低了响应时间,周转时间却很差,所以这里的问题是,通常我们对进程一无所知,应该如何构建调度程序来实现这些目标?调度程序如何在运行过程中学习进程的特征,从而作出更好的调度决策?
关键问题是:没有完备的知识如何调度?没有工作长度的先验知识,如何实现一个能够同时减少响应时间和周转时间的调度程序?这个问题的答案是,从历史中学习,多级反馈队列是用历史经验预测未来的一个典型例子,操作系统中有很多地方采用了这种技术(同样的问题存在于计算机科学领域的很多地方,比如硬件的分支预测以及缓存算法),如果工作有明显的阶段性行为,因此可以预测,这种方式会很有效.
MLFQ:基本规则
MLFQ中有许多独立的队列,每个队列有不同的优先级,任何时刻,一个工作只能存在与一个队列中.MLFQ总是优先执行较高优先级的工作,也就是在较高级队列中的工作.
当然,每个队列中可能有多个工作,因此具有同样的优先级,在这种情况下,就对这些工作采用轮转(round robin)调度策略.
因此,MLFQ调度策略的关键在于如何设置优先级.MLFQ没有为每个工作指定不变的优先级顺序而已,而是根据观察到的行为调整它的优先级.例如,如果一个工作不断放弃CPU去等待键盘输入,只是交互型进程的可能行为.MLFQ因此会让它保持高优先级.相反,如果一个工作长时间占用CPU,MLFQ会降低它的优先级,通过这种方式,MLFQ在进程运行过程中学习其行为,从而利用工作的历史来预测它未来的行为.
因此,我们得到了MLFQ的两条基本规则:
1.如果A的优先级大于B的优先级,运行A,不运行B
2.如果A的优先级等于B的优先级,论转运行A和B.
如下图所示,它可以看成是MLFQ调度策略某一个时刻的快照.
最高优先级有两个工作A和B,工作C,E,F位于中等有限级,而D的优先级最低,按照上面的介绍的基本规则,由于A和B有最高优先级,调度程序将交替的调度A和B,可怜的C,D,E,F将永远都没有机会运行,这有点气人.
在RTOS系统中,优先级一般是固定的,为了避免出现低优先级的任务饥饿的情况,要求每个任务都要主动的调用休眠接口,最常见的是sleep系统调用,它会将当前任务挂入系统休眠队列一段时间,让低优先级的任务有执行的机会,如下图,注意系统就绪对类和系统定时器队列之间任务的流转交互.