chapter 4 进程调度
4.1 多任务
4.2 linux的进程调度
- O(1)调度器:对大服务器的工做负载很理想,可是缺乏交互进程。
- 反转楼梯最后期限调度算法(RSDL)
- 彻底公平调度算法(CFS)
4.3 策略
4.3.1 进程分类
4.3.2 进程优先级
- 调度算法中最基本的一类就是基于优先级的调度:
- 优先极高的进程先运行;相同优先级的进程按照轮转方式进行调度
- 调度程序老是选择时间片未用尽并且优先级最高的进程运行。
- Linux采用了两种不一样的优先级范围:
- nice值(从-20——+19):默认值为0;数值越大意味着优先级越低;能够经过 ps-el查看系统进程列表并找到NI标记列对应的优先级
- 实时优先级(从0——99):越高的实时优先级级数意味着进程优先级越高
- 两者互不交互
4.3.3 时间片
- 时间片表示进程在被抢占前所能持续运行的时间。
- 调度策略必须肯定一个默认的时间片;
- Linux的CFS调度器并无直接划分时间片到进程,而是将处理器的使用比例划分给了进程。也就是说,其抢占时机取决于新的可执行程序消耗了多少处理器使用比,若是消耗的使用比比当前进程小,则新进程当即投入运行抢占当前进程。
4.3.4 调度策略的活动
4.4 linux调度算法
4.4.1 调度器类
- Linux调度器是以模块方式提供的(也就是调度器类),目的是容许不一样类型的进程能够有针对性地选择调度算法
- 调度器类容许多种不一样的可动态添加的调度算法并存,调度属于本身范畴的进程;
- 调度器代码会按照优先级顺序遍历调度类,拥有一个可执行进程的最高优先级的调度器类胜出,去选择下面要执行的那一个程序;
4.4.2 Unix系统中的进程调度
Unix使用的调度算法是分配绝对的时间片,这样就会引起固定的切换频率,不利于公平性。 而Linux采用的CFS彻底摒弃了时间片,分配给进程一个处理器使用比重,保证恒定的公平性和变更的切换频率。函数
4.4.3 公平调度(CFS)
- CFS的出发点基于一个简单的理念:进程调度的效果应当如同系统具有一个理想中的完美任务处理器。
-
CFS的作法以下:操作系统
- 容许每一个进程运行一段时间、循环轮转、选择运行最少的进程做为下一个运行进程;
- nice值做为进程得到的处理器运行比的权重 即:绝对的nice值再也不影响调度决策,它们的相对值才会影响处理器时间的分配比例——几何加权。
- 每一个进程都按照其权重在所有的可运行进程中所占的比例对应的“时间片”来运行
-
目标延迟:无限小调度周期的近似值设计
- 最小粒度:每一个进程得到的时间片底线,默认为1ms。
- 没有时间片概念可是仍需维持时间记帐。
4.5 linux调度的实现
——即CFS调度算法的实现。code
四个组成部分:队列
- 时间记帐
- 进程选择
- 调度器入口
- 睡眠和唤醒
4.5.1 时间记帐
- 全部的调度器都必须对进程的运行时间作记帐;
- CFS使用调度器实体结构来追踪运行记帐
- 1.调度器实体结构
- CFS使用调度器实体结构来追踪进程运行记帐:

- 2.虚拟实时
- CFS使用了vruntime变量来存放进程的虚拟运行时间,用来表示进程到底运行了多少时间,以及它还应该运行多久。
- 这个虚拟运行时间是加权的,与定时器节拍无关。
- 虚拟运行时间以ns为单位。


- 相关的函数是updatecurr(),它计算了当前进程的执行时间并存放入变量deltaexec中,而后又将运行时间传递给_updatecurr();
- _updatecurr()根据当前可运行进程总数对进行时间进行加权计算,最终将权重值与当前运行进程的vruntime值相加。

4.5.2 进程选择
- CFS算法核心:选择具备最小vrntime的任务
- 具体作法:利用红黑树rbtree(以节点形式存储数据的二叉树)
- 举例:
- 选择下一个任务:从根节点中序遍历二叉树,一直到叶子节点(也就是vrntime最小的进程);
- 向树中加入进程:在进程变为可执行状态或者经过fork()调用第一次建立进程;
- 从树中删除进程:发生在进程阻塞或者终止的时候
**Linux中,红黑树被称为rbtree,是一个自平衡二叉搜索树,是一种以树节点形式存储的数据,这些数据会对应一个键值,能够经过这些键值来快速检索节点上的数据,并且检索速度与整个树的节点规模成指数比关系。 **进程
-
挑选下一个任务:
节点键值是可运行进程的虚拟运行时间,进程选择算法是【运行rbtree树种最左边叶子节点所表明的那个进程】,函数是picknextentity() 

- 向树中加入进程:
- 发生在进程被唤醒或者经过fork调用第一次建立进程时。
- 函数enqueueentity():更新运行时间和其余一些统计数据,而后调用enqueueentity()。
函数enqueue_entity():进行繁重的插入工做,把数据项真正插入到红黑树中: 
-
从树中删除进程
4.5.3 调度器入口
- 进程调度的主要入口点是函数schedule(),定义在kernel/sched.c中;这正是内和其余部分用于调度进程调度器的入口
- 这一函数最重要的工做就是调用picknextstate(),依次检查每个调度类,并从最高优先级的调度类中,选择最高优先级进程
4.5.4 睡眠和唤醒
- 进程休眠必定是为了等待一些事件
- 进程把本身标记成休眠状态,从可执行红黑树中移除;
- 放入等待队列——由等待某些时间发生的进程组成的链表,内核用wakequeuehead_t来表明等待队列
3. 唤醒操做由函数wake_up()进行
- 它会调用函数try_to _wake_up()将进程设置为TASK_RUNNING状态,调用enqueue_task()将进程放入红黑树中
- 固然,也存在虚假唤醒进程的状态
4.6 抢占和上下文切换
参考资料
linux内核设计与实现