一篇文章让你了解Linux进程调度器( 二 )


根据进程的不同分类Linux采用不同的调度策略.
对于实时进程 , 采用FIFO或者Round Robin的调度策略.
对于普通进程 , 则需要区分交互式和批处理式的不同 。传统Linux调度器提高交互式应用的优先级 , 使得它们能更快地被调度 。而CFS和RSDL等新的调度器的核心思想是”完全公平” 。这个设计理念不仅大大简化了调度器的代码复杂度 , 还对各种调度需求的提供了更完美的支持.
注意Linux通过将进程和线程调度视为一个 , 同时包含二者 。进程可以看做是单个线程 , 但是进程可以包含共享一定资源(代码和/或数据)的多个线程 。因此进程调度也包含了线程调度的功能.
linux进程的调度算法其实经过了很多次的演变, 但是其演变主要是针对与普通进程的, 因为前面我们提到过根据进程的不同分类Linux采用不同的调度策略.实时进程和普通进程采用了不同的调度策略, 更一般的普通进程还需要启发式的识别批处理进程和交互式进程.
实时进程的调度策略比较简单, 因为实时进程值只要求尽可能快的被响应, 基于优先级, 每个进程根据它重要程度的不同被赋予不同的优先级 , 调度器在每次调度时, 总选择优先级最高的进程开始执行. 低优先级不可能抢占高优先级, 因此FIFO或者Round Robin的调度策略即可满足实时进程调度的需求.
但是普通进程的调度策略就比较麻烦了, 因为普通进程不能简单的只看优先级, 必须公平的占有CPU, 否则很容易出现进程饥饿, 这种情况下用户会感觉操作系统很卡, 响应总是很慢.
此外如何进程中如果存在实时进程, 则实时进程总是在普通进程之前被调度
3、linux调度器的演变一开始的调度器是复杂度为O(n)O(n)的始调度算法(实际上每次会遍历所有任务 , 所以复杂度为O(n)), 这个算法的缺点是当内核中有很多任务时 , 调度器本身就会耗费不少时间 , 所以 , 从linux2.5开始引入赫赫有名的O(1)O(1)调度器
然而 , linux是集全球很多程序员的聪明才智而发展起来的超级内核 , 没有最好 , 只有更好 , 在O(1)O(1)调度器风光了没几天就又被另一个更优秀的调度器取代了 , 它就是CFS调度器Completely Fair Scheduler. 这个也是在2.6内核中引入的 , 具体为2.6.23 , 即从此版本开始 , 内核使用CFS作为它的默认调度器 , O(1)O(1)调度器被抛弃了, 其实CFS的发展也是经历了很多阶段 , 最早期的楼梯算法(SD), 后来逐步对SD算法进行改进出RSDL(Rotating Staircase Deadline Scheduler), 这个算法已经是”完全公平”的雏形了 ,  直至CFS是最终被内核采纳的调度器, 它从RSDL/SD中吸取了完全公平的思想 , 不再跟踪进程的睡眠时间 , 也不再企图区分交互式进程 。它将所有的进程都统一对待 , 这就是公平的含义 。CFS的算法和实现都相当简单 , 众多的测试表明其性能也非常优越 。

一篇文章让你了解Linux进程调度器

文章插图
 
4、Linux的调度器设计4.1 linux进程调度器的框架
2个调度器
可以用两种方法来激活调度
  • 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU
  • 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测是否有必要
因此当前linux的调度程序由两个调度器组成:主调度器 , 周期性调度器(两者又统称为通用调度器(generic scheduler)或核心调度器(core scheduler))
并且每个调度器包括两个内容:调度框架(其实质就是两个函数框架)及调度器类
6种调度策略
linux内核目前实现了6中调度策略(即调度算法), 用于对不同类型的进程进行调度, 或者支持某些特殊的功能
比如SCHED_NORMAL和SCHED_BATCH调度普通的非实时进程, SCHED_FIFO和SCHED_RR和SCHED_DEADLINE则采用不同的调度策略调度实时进程, SCHED_IDLE则在系统空闲时调用idle进程.
idle的运行时机
idle 进程优先级为MAX_PRIO , 即最低优先级 。
早先版本中 , idle是参与调度的 , 所以将其优先级设为最低 , 当没有其他进程可以运行时 , 才会调度执行 idle
而目前的版本中idle并不在运行队列中参与调度 , 而是在cpu全局运行队列rq中含idle指针 , 指向idle进程, 在调度器发现运行队列为空的时候运行, 调入运行


推荐阅读