Linux O(1)调度器和CFS

Linux中的调度

Linux系统的线程是内核线程,所以Linux系统的调度是基于线程的,而不是基于进程的。

为了进行调度,Linux系统将线程区分为三类。

  • 实时先入先出
  • 实时轮转
  • 分时

实时先入先出线程具有最高优先级,它不会被其他线程抢占,除非那是一个刚刚准备好的,拥有更高优先级的实时先入先出线程。实时轮转线程与实时先入先出线程基本相同,只是每个实时轮转线程都有一个时间量,时间到了之后就可以被抢占,如果多个实时轮转线程都准备好了,每一个线程运行它的时间量所规定的事件,然后插入到实时轮转线程列表的末尾。事实上,这两类线程都不是真正的实时线程。执行的最后期限无法确定,更无法保证最后期限前线程可以执行完毕。这两类线程比起分时线程来说只是具有更高的优先级而已。Linux系统之所以称它们为”实时“是因为Linux遵循的标准使用了这个名称。在系统内部,实时线程的优先级从0到99,0是实时线程的最高优先级,99是实时线程的最低优先级。

Linux的两个调度算法

这两个调度算法,它们的内部与调度队列的设计密切相关,该调度队列是一个关键的数据结构,可以通过调度器来跟踪系统中的所有可运行的任务,并选择下一个要运行的任务进行调度。调度队列与系统中的每一个CPU都相关。

Linux O(1)调度器

命名这个名字是因为它能够在常数时间内执行任务调度,例如从执行队列中选择一个任务或将一个任务加入执行队列,这与系统中的任务总数无关。在O(1)调度器中,调度队列被组织成两个数组,一个是任务正在活动的数组,一个是任务过期失效的数组。如图10-10所示,每个数组都包含了140个链表头,每个链表具有不同的优先级。链表头指向给定优先级的双向进程链表。调度的基本操作如下所述。

调度器从正在活动数组中选择一个优先级最高的任务。如果这个任务的时间片(时间量)过期失效了,就把它移动到过期失效数组中(可能会插入到优先级不同的列表中)。如果这个任务阻塞了。比如说正在等待I/O事件,那么在它的时间片过期失效之前,一旦所等待的事件发生,任务就可以继续运行,它将被放回到之前正在活动的数组中,时间片根据它所消耗的CPU事件相应的减少。一旦它的时间片消耗殆尽,它也会被放到过期失效数组中。当正在活动数组中没有其他的任务了,调度器交换指针,使得正在活动数组变为过期失效数组,过期失效数组变为正在活动数组。这种方法可以保证低优先级的任务不会被饿死(除非实时先入先出线程完全占用CPU,但是这种情况是不会发生的)。

不同的优先级被赋予不同的时间片长度,高优先级的进程拥有较长的时间片。例如,优先级为100的任务可以得到800ms的时间片,而优先级为139的任务只能得到5ms的时间片。

在这里插入图片描述
这种调度模式的思想是为了使进程更快地出入内核。如果一个进程试图读取一个磁盘文件,在调用read函数之间等待一秒钟的时间显然会极大地降低进程的效率。每个请求完成之后让进程立即运行的做法会好得多,同时这样做也可以使下一个请求更快完成。相似的,如果一个进程因为等待键盘输入而阻塞。那么它很明显是一个交互进程,这样的进程只要准备好运行后就应当被赋予较高的优先级,从而保证交互进程可以提供较好的服务。在这种情况下,当I/O密集型进程和交互进程被阻塞之后,CPU密集型进程基本上可以得到所有被留下的服务。

由于Linux系统事先不知道一个任务究竟是I/O密集型的,还是CPU密集的,它只是依赖于连续保持的交互启发式方法。通过这种方式,Linux系统区分静态优先级和动态优先级,线程的动态优先级不断地被重新计算,其目的在于(1)奖励互动进程(2)惩罚占用CPU的进程。在Linux O(1)调度中,最高的优先级是-5,是从调度器接受的与更高优先级相对应的较低优先级的值,最高的优先级惩罚是+5。

上面描述的内容说明了通过访问正在活动数组就可以做出调度决定,那么调度可以在一个固定的时间O(1)完成,而与系统中的进程数量无关,然而,除了常数时间操作表现出来的高性能之外,O(1)调度器有明显的缺点。最值得注意的是,利用启发式方法来确定一个任务的交互性,会使该任务的优先级复杂而不完善,从而导致在处理交互任务时性能很糟糕。

完全公平调度器CFS

为了改进上面的缺点,提出了完全公平调度器(Completely Fair Scheduler,CFS)

CFS的主要思想是使用一棵红黑树作为调度队列的数据结构。根据任务在CPU上运行的时间长短而将其有序地排列在树中,这种事件称为虚拟运行时间。CFS采用ns级的力度来说明任务的运行时间。如图10-10b所示,树中的每个内部节点对应于一个任务。左侧的子节点对应于在CPU上运行时间更少的任务,因此左侧的任务会更早的被调度,右侧的子节点是哪些迄今消耗CPU时间较多的任务,叶子节点在调度器中不起任何作用。

CFS调度算法可以总结如下,该算法总是优先调度那些使用CPU时间最少的任务,通常是在树种最左边节点上的任务。CFS会周期性地根据任务已经运行的时间,递增它的虚拟运行时间值,并将这个值与树中当前最左节点的值进行比较,如果正在运行的任务仍具有较小虚拟运行时间值,那么它将继续运行,否则,它将被插入红黑树的适当位置,并且CPU将执行新的做最左边节点上的任务。

考虑到任务有优先级的差异和”友好程度“,因而当一个任务在CPU上运行时,CFS会改变该任务的虚拟运行时间六十的有效速率。对于优先级较低的任务,时间流逝更快,它的虚拟运行时间值也将增加得更快,考虑到系统还有其他任务,因此有较低的优先级的任务会失去CPU的使用权,相较于优先级高的任务更快地重新插入树中。以这种方式,CFS可避免使用不同的调度队列结构来放置不同优先级的任务。

总之,选择一个树种的节点来运行的操作可以在常数时间内完成,然而在调度队列中插入一个任务需要 O ( l o g ( N ) ) O(log(N)) O(log(N))的时间,其中 N N N是系统中的任务数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值