CPU(进程)调度
评判指标
响应时间小(点了就有反应)
周转时间(尽快完成任务)
吞吐量(系统内耗小)
吞吐量与响应时间有矛盾:响应时间小则切换次数多则系统内耗大则吞吐量小
前台任务与后台任务关注点也不同:前台任务关注响应时间(如word输入了几秒才看到屏幕出现字显然不合理),后台任务关注周转时间(比如编译一段简单的程序很久才编译完也不合理)。
CPU约束型任务:很长时间没有IO(比如Gcc,matlab算矩阵)
I/O约束型任务:I/O很多,往往是前台任务。
折中时应该让IO约束型任务有更高的优先级,IO任务经常需要等待,这个时候就可以切出去执行CPU约束任务,反之CPU约束型任务一执行就不出来IO约束型任务就很尴尬了。
进程调度方式
调度方式分为抢占式和非抢占式
非抢占式一旦把CPU分配给某个进程后就会让它一直运行下去,直到运行完毕或者因为某种事件(不包括时钟中断)被阻塞才会把处理器分配给别的进程。此种方式可能引起进程调度的因素大致有:
1.执行完毕 2. IO请求 3 进程通信或者同步过程中执行了某种原语操作。
此种调度实现简单,系统开销小,适用于大多数批处理系统环境,但是难以满足实时性要求。
抢占式允许调度程序根据某种原则暂停某一个正在执行的程序并将它的处理器资源分配给另一个进程。它可以防止一个进程长时间占用处理机,可以为大多数进程提供更公平的服务,特别是能满足对响应时间有严格要求的实时任务的需求。抢占原则大致有:
1.优先权原则对紧急任务赋予更高的优先级,允许优先级高的程序抢占优先级更低的程序。
2.短作业优先原则。期望执行时间短的进程允许抢占期望执行时间长的进程。
3.时间片原则,各个进程按照时间片轮转运行,当一个进程的时间片用完后引发调度。这种原则适用于分时系统,大多数实时系统,以及要求较高的批处理系统。
调度算法
由于短作业长作业前台任务后台任务没有先验知识,调度算法需要有一定的学习能力,做到不同的任务区别对待。
调度算法大致可以分为:
先到先服务这种算法对CPU约束型任务有利而对IO约束型任务不利
短作业优先这种算法可以有效降低作业的平均等待时间,提高系统的吞吐量。但是该算法对长作业不利,且作业的长短其实跟估计值并不完全一致很难做到真正的短作业优先。也未考虑作业的紧迫程度,无法保证紧迫作业即使处理。
优先级调度这种算法可以照顾紧迫型作业,关键在于使用静态优先级还是动态优先级;是否允许抢占。一种高响应比的优先级制定策略是:
优先级=(等待时间+要求服务时间)/要求服务时间
此种策略可以实现:若等待时间相同,则短作业优先。若要求服务时间相同,则等待时间越长优先级越高(即先到先服务);对于长作业,随着其等待时间的增加其优先级会越来越大。如此既照顾了短作业又照顾了长作业,缺点是每一次调度前都需要重新计算响应比,系统内耗大。
时间片轮转
时间片轮转是给每一个进程分配一个时间片,时间片用完则引发调度,如此可以保证每一个进程都能在一段时间内获得一定的时间片运行,系统可以在给定的时间内响应所有用户的请求。这种方法的时间片大小确定很关键,小了则响应时间短而频繁引发切换,大了则退化为先到先服务。
多级反馈队列调度
多级反馈队列调度算法是目前公认的较好的进程调度算法,它:
1、设置多个就绪队列,并且为每一个队列设置不同的优先级,且赋予每一个队列中进程的时间片大小也不同,优先级越高的队列其进程的时间片越小。
2、当一个新的进程进入内存后,先把它放到第一就绪队列(最高优先级)末尾FCFS排队等待。若它在给定时间片未执行完则放到第二就绪队列末尾,如此逐级降低。当一个长作业从第一队列依次降到第n队列后,在第n队列就按照时间片轮转来调度。
3、仅当第一队列空闲时,才从第二队列选择进程调度,如此递推。如果处理器在执行第i队列中的进程时有新进程进入更高优先级的队列中,则正在运行的进程被放入该队列末尾转而执行新到的更高优先级的进程。
低级调度
低级调度用于决定引发切换时,就绪队列中哪一个进程(或是内核级线程)被执行。是最基本的调度,在多道批处理系统、分时系统和实时系统中都必须配置这种调度。
它必须要实现的功能有:
1.保持CPU现场信息,如程序计数器、通用寄存器等中的内容,将它们送入响应进程的PCB单元保存起来。
2.按照某种算法从就绪队列选取一个进程,将他的状态修改为运行态。
3.将CPU分配给此进程开始执行。此时需要为该被选中进程恢复现场,即把它的PCB中的相关信息装入CPU的各个寄存器。
其中步骤1、3的实现如上图,后面重点说步骤2的选择算法如何设计。应当指出,进程切换将花费不少时间,即使现代计算机,完成一次上下文切换也需要花费几毫秒时间,这些时间可以执行上千条指令了。现在已经有通过硬件方法(采用两组或以上寄存器组)来减少上下文切换时间的处理器了。
一个实际低级调度算法
核心就是找到一个next进程,然后切换到next继续执行。
1.给每一个进程分配一个初始的优先级数,用counter作为优先级和时间片。
2.根据程序运行的时间更新counter(counter-=time)
<如此操作,前台任务因为IO被阻塞的,它的counter还剩余,而后台任务因为时间片用完被阻塞的则不会剩余>
3.引发调度时,先找到就绪态进程中counter最大的进程,若最大counter非零就选择此进程。
4.若就绪态最大counter为零,则让所有进程的counter均除以2再加上初始counter。
<如此操作可以使得非就绪态的进程获得更大的counter,当它们进入就绪态以后会获得更高的优先级(就绪态进程的counter会被重置为初始值,而非就绪态进程会使初始值加上当前值的一半)。在阻塞队列中呆的越久他进入就绪队列后counter越大,如此实现了学习能力,前台进程往往会执行IO被阻塞,获得更大的counter。除以2是构造几何级数保证最大counter收敛于2p。>
中级调度
引入中级调度主要是为了提高系统的吞吐量。使得那些暂时不能运行的进程不再暂用内存而把他们转移到外存上去等待,此时的进程状态称为挂起态。当它们再具备运行条件且内存有空闲时,由中级调度程序将其转移到内存,修改状态为就绪态挂载在就绪队列中,中级调度实际是内存置换。