《操作系统导论》任务调度

《操作系统导论》任务调度

Tags
OS
Published
2022-12-23
Author
宿愿Cc

先进先出(First In First Out, FIFO)

这个方式是哪个任务先准备好就先运行哪个程序,但是有可能会遇到其中一个任务耗时很长,后面排队的任务耗时很短,但是确需要排很久队才能得到运行。
 

最短任务优先(Shortest Job First, SJF)

这个方式会让最短耗时的任务优先执行,但是这个前提是假设所有任务都在排队,如果这些任务都是不确定时间到达的,则后面到达的耗时较短的任务依旧可能要等待耗时较长的任务。
 

最短完成时间优先(Shortest Time-to-Completion First, STCF)

这个方式是在最短任务优先的基础上加上了抢占式,如果后到达的任务耗时比正在运行的任务所耗时更短,则会优先运行后到达的任务。
 

响应时间

执行任务的时候另一个需要关注是响应时间
 

轮转

通过采用时间片轮转的方式,来让每个任务都运行一定的时间,已达到对响应时间敏感的调度程序。
轮转(Round-Robin, RR)有时候也被称为时间切片(time-slicing), 时间切片的长度必须是时钟中断周期的倍数。如时钟中断是每10ms中断一次,则时间片可以是10ms,20ms,30ms的任何其它倍数。
notion image
时间片长度对于RR是至关重要的,太短的时间片长度会导致上下文切换频繁,因此,操作系统需要权衡时间片的长度,使其足够长,以便摊销(amortize)上下文切换的成本
当系统某些操作有固定成本时,通常会采用摊销(amortization)的技术,即执行较少次的操作,系统的总成本就会降低。如时间片设置为10ms,并且上下文的切换为1ms,那么需要浪费大约10%的时间来进行上下文切换,但是如果时间片设置为100ms,那么只需要1%的时间来进行上下文切换,因此时间片带来的成本就被摊销了。
需要注意的是,上下文切换的成本不仅仅是保存和恢复少量的寄存器,程序在运行时,它们会在CPU高速缓存,TLB,分支预测器和其它片上的硬件中建立了大量的状态。这在切换到另一个工作时此状态会被刷新,且与当前运行的作业相关的新状态被引入。
 

多级反馈队列(Multi-level Feedback Queue, MLFQ)

这个方式像是采用优先级队列的方式来管理程序,给程序分不同的优先级,优先运行优先级较高的程序,如果高优先级有多个程序,则轮转运行这些优先级较高的程序
 

如何改变优先级

默认所有的新任务优先级都是最高,当它运行完一个时间片时候,将其优先级往下调一个等级,直到最低。
如果是频繁需要用户键盘输入的任务,如每1ms都需要进行一次i/o操作则在每次触发i/o的时候不改变优先级,依旧保持最高。这样便可保证可交互性的工作快速运行。
但是这依旧会有一些问题,如饥饿问题,假设有太多交互性的任务,那他们就会一直占着CPU,那么其它任务就永远得不到执行(它们饿死了)。
一些程序员会愚弄调度程序(game the scheduler),通过一些手段来欺骗调度程序,让它得到远超公平的资源,如在时间片用完之前,主动调用一个i/o操作(如访问一个无关的文件),从而主动释放CPU。
 

提升优先级

为了避免这个问题,可以通过定期将最低优先级的任务重新提到最高优先级的方式来让低优先级的任务能够重新得到执行,如在S时间后将低优先级的任务重新提到最高优先级,但是这个S的值设置的也很讲究,如果设置的太短,则交互性的工作会得不到合适的CPU时间比例,如果设置的太长,则长耗时的工作又会饿死,John Ousterhout 曾将这种值称为”巫毒常量”,因为似乎需要一些黑魔法才能正确设置。如下右图,每隔50ms提升一次优先级
notion image
 

更好的计时方式

我们可以采用一种新的方式来避免程序一直霸占高优先级的队列,我们记录每个程序在每个队列的运行时长,只要达到了特定的时长,则将其优先级降低到下一级,无论这个程序是否有主动放弃过CPU。
notion image
 

MLFQ调优

关于 MLFQ 调度算法还有一些问题。其中一个大问题是如何配置一个调度程序,例如, 配置多少队列?每一层队列的时间片配置多大?为了避免饥饿问题以及进程行为改变,应该多久提升一次进程的优先级?这些问题都没有显而易见的答案,因此只有利用对工作负载的经验,以及后续对调度程序的调优,才会导致令人满意的平衡。
大多数MLFQ都支持不同队列的可变时间片长度,一般都是优先级较高的队列只有较短的时间片(如10ms或者更少),而优先级越低,则时间片越长,如下图:
notion image
还有一些MLFQ的调度没有用表(类似于我们编写的环境变量文件,来配置一些基本参数),也没有用我们上述学习的各种方式,有些是根据CPU和数学公式来调整优先级,还有些是将最高优先级队列只给操作系统使用,用户程序是没法使用的。
 

调度:比例份额

比例份额调度也被称为公平份额(fair-share)调度程序, 它的作用是为每个工作获得一定比例的CPU时间。
比例份额有一个很优秀的现代例子,名为彩票调度(lottery sheduling),这个想法是这样的,每隔一段时间,就举行一次彩票抽奖,以确定接下来应该运行哪个进程,越是应该频繁运行的进程,越是应该拥有更多赢得彩票的机会。
 

彩票调度

彩票数(ticket)代表了进程占有某个资源的份额,一个进程拥有的彩票数占总彩票数的百分比,就是它占有资源的份额。
例如两个进程A、B, A进程拥有75张彩票,而B 拥有25张彩票,那么A进程就拥有75%的的CPU时间。