什么是调度
在多道程序设计中,当多个进程或线程同时想要使用同一个CPU,就会出现竞争现象。这时候就需要一个程序来协调它们以确定谁可以获得CPU的使用权,这个程序就是调度程序,调度程序使用的算法就是调度算法。
调度概述
早期的批处理系统时代,CPU是稀缺资源,我们时常需要同时处理多个批处理任务或者与用户交互的任务,一个好的调度程序在提高性能和用户满意度方面是至关重要的,因此,人们花费了大量的时间在创造聪明而有效的调度算法上。
但是,随着技术的演进,这一情况发生了变化,首先,PC机上没有那么多需要同时运行的进程,所以调度算法不需要考虑太多,其次,CPU的速度越来越快,有限制的更多的是人类敲击键盘的速度,因此调度算法也不需要进行太多的决策。
但是,在网络世界,这点又有些不同,这里,多个进程经常同时竞争CPU,此时,调度程序再次变得十分重要。
此外,还要考虑CPU的利用率,因为进程切换的代价是十分高昂的。
- 进程行为
几乎所有的进程都是I/O请求与计算交替突发的,也就是说,进程要么在计算,要么在等待I/O,如果进程把大部分时间花费在计算上,那么它就是计算密集型,如果它把大部分时间花费在等待I/O上,那么它就是I/O密集型。 - 何时调度
第一,在创建一个新进程之后,需要运行父进程还是子进程,决策是,可以任意选择。
第二,当进程被阻塞在I/O或者信号量上时,必须选择另一个进程运行。
第三,当一个进程退出时,必须选择一个进程运行。
第四,当一个I/O中断发生时,必须做出调度决策。当I/O中断完成,那么被阻塞在这个I/O上的进程就变成就绪进程,接下来是继续运行当前进程,还是运行这个因为I/O完成而变成就绪态的进程,需要调度程序来进行决策。
根据如何处理时钟中断,可以将调度算法分为两类,非抢占式和抢占式。
非抢占式:非抢占式调度算法挑选一个进程让它执行,直到它被阻塞或者自行释放CPU,或者一个更高优先级的进程到来,否则即使发生时钟中断,它仍然继续运行。
抢占式:抢占式调度算法为一个进程设定一个最大运行时间,如果超过这个最大运行时间,进程仍然没有退出,就会被挂起。 - 调度算法分类
毫无疑问,不同的环境需要不同的调度算法,这是因为,在不同的应用领域有不同的目标,据此,我们把调度程序划分为三类
1)批处理
2)交互式
3)实时
批处理的特点是没有太强的时间限制,每个进程都愿意等待。所以,在这种场合,非抢占式或者长时间周期的抢占式算法通常都是可接受的,这种调度算法没有过于频繁的进程切换,所以CPU利用率很高。
交互式的特点是很多个进程都要同时并发执行,以服务器为例,每个客户都想要尽快得到请求应答,因此抢占是必须的,没有人愿意长时间等待。
实时的特点是算法只执行当前推进现有应用的程序,所以,由于每个进程都意识到自己可能长时间得不到CPU,所以它们都会快速地完成自己的工作并进入阻塞。 - 调度算法的设计目标
公平:给每个进程公平的CPU时间
策略强制执行:看到策略规定的情况,调度程序就要强制执行策略所规定的内容。
平衡:保持系统的所有部分忙碌
批处理系统中的调度
-
先来先到服务
进程按照它们请求CPU的顺序使用CPU,每个进程除非被阻塞,否则就一直运行到结束。
这里有一些问题。举个例子,假设有一个计算密集型进程和多个I/O密集型进程,计算密集型会占用CPU 1S,当计算密集型进程计算完成之后,I/O密集型进程立马完成计算,然后读取磁盘块,接着排在计算密集型进程后面,这过程中,I/O密集型进程花费了1S的时间,其中绝大部分时间是在等待计算密集型进程完成那一秒钟的计算。如果这种I/O密集型需要完成1000次磁盘读取才算完成工作,那么这种进程就需要1000S才能完成工作。如果采用抢占式调度算法,情况就不一样了,如果采用10ms一次的中断,那么I/O密集型进程完成整个工作只需要10S,而且不会对计算密集型进程造成很严重的延迟。 -
最短作业优先
就是让占用CPU时间最短的那个程序优先执行,然后整体的平均周转周期就会变少。举个例子,一个进程耗时10分钟,一个耗时5分钟,如果10分钟的那个先执行,5分钟的那个后执行,那么10分钟的就花了10分钟,5分钟的就花了15分钟(10分钟在等待),这样他们就一共花了25分钟,但是如果让5分钟的先开始,那么他们就是花了20分钟,这样就少了5分钟,这种时间的少在外界看来是不存在的,但是在协调两者的调度算法来看,这是存在的,尽量缩小进程等待的时间,不让进程在那里进行那些无意义的等待。 -
最短剩余时间优先
最短作业优先的抢占式版本是最短剩余时间优先,当一个新作业到达的时候,调度程序将其运行所需剩余时间与当前进程运行所需剩余时间进行比较,选择其中小的运行,这种方式可以使新的短作业获得良好的服务。
交互式系统中的调度
-
轮转调度
这是一种最古老、最简单、最公平且使用最广泛的调度算法。每个进程被分配一个时间片,进程执行时间如果超过这个时间或者进程发生阻塞,它就会被挂起。这种调度算法很容易实现,调度程序需要做的是维护一张可运行进程列表,当一个进程用完它的时间片后,它就会被放到表尾。
但是,这里面有一个时间片长短的问题,如果时间片太长,就会让一个进程等待时间过长,影响交互式系统中用户的体验,如果时间片太短,就会导致CPU花费太多时间在进程切换上,从而降低CPU利用率。因此,需要选择一个合适的时间片长度。 -
优先级调度
上面的轮转调度是基于每个进程同等重要这样一个事实。但实际上,这是不行的,毕竟世界上没有这样绝对的公平,我们必须维持一定的不公平才能实现长久的公平与稳定。所以进程中有优先级的概念,优先级高的进程会被有限执行,同优先级的进程会采用轮转调度。 -
多级队列
-
最短进程优先
-
保证调度
-
彩票调度
-
公平分享调度
策略和机制
机制,就是具体的调度算法,不如前面的抢占式,非抢占式,轮转调度,优先级调度,多级队列等等。
策略,就是我如何安排这些进程的优先级,用户在编写程序的时候可以根据自己的需求来安排进程的优先级。
机制具有强制性,策略具有易变性。
线程调度
分为两类,用户级线程和内核级线程。
对于用户级线程来说,由于内核并不知道有线程的存在,所以进程得到多少CPU时间完全由用户控制,即使他用完了分配给这个进程的所有CPU时间,也不会影响其它的进程,因为内核分配CPU时间是以进程为单位的。
内核级线程调度就不会考虑这些,它对线程的调度顺序可能是一会儿这个进程中的线程,一会儿那个进程中的线程,但是如果两个线程的是平等的,那么进程会优先选择那个与当前线程所处进程相同的线程。