1.进程调度的概念
- 进程调度程序可以看做在可运行态进程之间分配有限的处理器时间资源的内核子系统。调度程序是像linux这样多任务操作系统的基础。只有通过调度程序的合理调度,系统资源才能最大限度的发挥作用,多进程才会有并发执行的效果
- linux提供了抢占式的多任务模式
- 进程在被抢占之前能够运行的时间是预先设置好的,而且有个专门的名字,叫进程的时间片
1.2进程的状态
运行状态 可以被运行 就绪状态 进程切换只有在运行状态
可中断状态 可以被信号中断 使其变成RUNNING
不可中断睡眠状态 智能被wakeup所唤醒
暂停状态 收到SIGSTOP SIGTSTP SIGTTIN时暂停
僵死状态 进程停止运行,但是父进程还没有将其清空
2.进程调度:
void schedule() 进程调度函数
switch_to(next) 进程切换函数
LInux中使用新的CFS调度器,其抢占时机取决于新得可运行程序消耗了多少处理器使用比。如果消耗的使用比当前进程小,则新进程立刻投入运行,抢占当前进程。否则,将推迟其运行。
2.1进程切换
switch_to
把进程切换为当前执行进程
1.将需要切换的进程赋值给当前进程指针
2.进行进程的上下文切换 (把CPU中tss寄存器的值换成新进程的)
上下文:程序运行时 CUP的特殊寄存器 通用寄存器(TSS)等信息+当前堆栈中的信息
当某个进程想要访问CPU资源的时候,碰巧CPU资源被占用,那么就会调用SLEEPON函数,把进程休眠。
3.Linux调度算法
- 调度器类
Linux调度器类是以模块方式提供的,这样做的目的是允许不同类型的进程可以针对性地选择调度算法。
这种模块化结构被称为调度器类。它允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程,每个调度器都有一个优先级,它会按照优先级顺序遍历调度类,拥有一个可执行进程的最高级的调度器胜出。
- 完全公平调度(CFS)
理念:进程调度的效果应如同系统具备一个理想中的完美多任务处理器。
完美的多任务处理器模型应该是这样的:我们能在10ms内同时运行两个进程,它们各自使用处理器一半的能力。
当然,上述理想模型并非现实,因为我们无法在一个处理器上真的同时运行多个进程。而且如果每个进程运行无限小的时间周期也是不高效的----因为调度时进程抢占会带来一定的代价:将一个进程换出,另一个换入本身有消耗,同时还会影响到缓存的效率。
CFS的做法是允许每个进程允许一段时间,循环轮转,选择运行最少的进程作为下一个运行进程,而不再采用分配给每个进程时间片的做法了,CFS在所有可运行进程总数基础上计算出一个进程应该运行多久,而不是依靠nice值来计算时间片。
总结来说,任何进程所获得的处理器时间是由它自己和其他所有可运行进程nice值的相对差值决定的。nice值对时间片的作用不再是算数加权,而是几何加权。任何nice值对象的绝对时间不再是一个绝对值,而是处理器的使用比。CFS称为公平调度器是因为它确保给每个进程公平的处理器使用比。正如我们知道的,CFS不是完美的公平,它是近乎完美的多任务。但是它确实在多进程的环境下,降低了调度延迟带来的不公平性。
- CFS由四个组成部分(时间记账,进程选择,调度器入口,睡眠和唤醒)
- 时间记账
所有的调度器都必须对进程运行时间做记账。分配一个时间片给每一个进程,那么当每次系统时钟节拍发生时,时间片都会减少一个节拍周期。当一个进程的时间片被减少到0时,它就会被另一个尚未减到0的时间片可运行进程抢占。
- 调度器实体结构
CFS使用调度器实体结构(定义在文件<linux/sche.h>的struct_sched_entity中)来追踪进程运行记账:
调度器实体结构作为一个名为se的成员变量,嵌入在进程描述符struct task_struct 内
2.虚拟实时
vruntime变量存放进程的虚拟运行时间。
CFS使用vruntime变量来记录一个程序到底运行了多长时间以及它还应该运行多久
- 进程选择
当CFS需要选择下一个运行进程时,它会挑一个具有最小vruntime的进程。其实这就是CFS调度算法的核心:选择具有最小vruntime的任务。那么剩下的内容我们就来讨论到底是如何实现选择具有最小的vruntime的进程。
CFS的进程选择算法可简单总结为“运行rbtree树中最左边叶子节点所代表的那个进程”
- 调度器入口
进程调度的主要入口点是函数schedule(),它定义在文件kernel/sched.c中。
- 睡眠和唤醒
休眠(被阻塞)的进程处于一个特殊的不可执行的状态。
- 等待队列
- 唤醒
- 抢占和上下文切换
上下文切换,也就是从一个可执行进程切换到另一个可执行进程,由定义在kernel/sched.c中的context_switch()函数负责处理。
- 用户抢占
用户抢占在以下情况时产生:
1.从系统调返回用户空间时
2.从中断处理程序返回用户空间时
- 内核抢占
与其他大部分的Unix变体和其他大部分的操作系统不同,Linux完整地支持内核抢占。只要重新调度是安全的,内核就可以在任何时间抢占正在执行的任务。
内核抢占会发生在:
1.中断处理程序正在执行,且返回内核空间之前。
2.内核代码再一次具有可抢占性的时候
3.如果内核中的任务显示地调用schedule()
4.如果内核中的任务阻塞(这同样也会导致调用shcedule())
- 实时调度策略
Linux提供了两种调度策略:SCHED_FIFO和SCHED_RR.