1、CFS的基本思路
在CFS算法引入之前,Linux使用过几种不同的调度算法,一开始的调度器是复杂度为O(n)的始调度算法(实际上每次会遍历所有任务,所以复杂度为O(n)), 这个算法的缺点是当内核中有很多任务时,调度器本身就会耗费不少时间,所以,从linux2.5开始引入赫赫有名的O(1)调度器,然而,linux是集全球很多程序员的聪明才智而发展起来的超级内核,没有最好,只有更好,在O(1)调度器风光了没几天就又被另一个更优秀的调度器取代了,它就是CFS调度器Completely Fair Scheduler. 这个也是在2.6内核中引入的,具体为2.6.23,即从此版本开始,内核使用CFS作为它的默认调度器,O(1)调度器被抛弃了。:
O(n)调度:内核调度算法理解起来简单:在每次进程切换时,内核依次扫描就绪队列上的每一个进程,计算每个进程的优先级,再选择出优先级最高的进程来运行;尽管这个算法理解简单,但是它花费在选择优先级最高进程上的时间却不容忽视。系统中可运行的进程越多,花费的时间就越大,时间复杂度为O(n)
O(1)调度:其基本思想是根据进程的优先级进行调度。进程有两个优先级,一个是静态优先级,一个是动态优先级.静态优先级是用来计算进程运行的时间片长度的,动态优先级是在调度器进行调度时用到的,调度器每次都选取动态优先级最高的进程运行.由于其数据结构设计上采用了一个优先级数组,这样在选择最优进程时时间复杂度为O(1),所以被称为O(1)调度。
这两种调度算法,其基本思路都是通过一系列运行指标确定进程的优先级,然后根据进程的优先级确定调度哪个进程,而CFS则转换了一种思路,它不计算优先级,而是通过计算进程消耗的CPU时间(标准化以后的虚拟CPU时间)来确定谁来调度。从而到达所谓的公平性。
绝对公平性:
cfs定义了一种新的模型,其基本思路很简单,他把CPU当做一种资源,并记录下每一个进程对该资源使用的情况,在调度时,调度器总是选择消耗资源最少的进程来运行。这就是所谓的“完全公平”。但这种绝对的公平有时也是一种不公平,因为有些进程的工作比其他进程更重要,我们希望能按照权重来分配CPU资源。
相对公平性:
为了区别不同优先级的进程,就是会根据各个进程的权重分配运行时间(权重怎么来的后面再说)。进程的运行时间计算公式为:
分配给进程的运行时间 = 调度周期 * 进程权重 / 所有进程权重之和 (公式1)
调度周期很好理解,就是将所处于TASK_RUNNING态进程都调度一遍的时间。
举个例子来说明一下,比如系统中只两个进程A, B,权重分别为1和2,假设调度周期设为30ms,那么分配给A的CPU时间为:30ms * (1/(1+2)) = 10ms;而B的CPU时间为:30ms * (2/(1+2)) = 20ms。那么在这30ms中A将运行10ms,B将运行20ms。
2、实现原理
在实现层面,Linux通过引入virtual runtime(vruntime)来完成上面的设想,具体的,我们来看下从实际运行时间到vruntime的换算公式
vruntime = 实际运行时间 * 1024 / 进程权重 。 (公式2)
实际上vruntime就是根据权重将实际运行时间标准化,标准化之后,各个进程对资源的消耗情况就可以直接通过比较vruntime来知道,比如某个进程的vruntime比较小,我们就可以知道这个进程消耗CPU资源比较少,反之消耗CPU资源就比较多。
有了vruntime的概念后,调度算法就非常简单了,谁的vruntime值较小就说明它以前占用cpu的时间较短,受到了“不公平”对待,因此下一个运行进程就是它。这样既能公平选择进程,又能保证高优先级进程获得较多的运行时间。这就是CFS的主要思想了。
或者可以这么理解:CFS的思想就是让每个调度实体(没组调度的情形下就是进程,以后就说进程了)的vruntime互相追赶,而每个调