一、基本概念
通过虚拟处理器和虚拟内存技术,让进程觉得自己在独享处理器,并且在分配和管理内存时候感觉可以使用系统的所有内存资源
但是实际上进程的数目远大于处理器的数目,进程是交替运行的,进程调度就是选择一个进程并分配有限时间资源到CPU上执行
二、调度策略
1、进程分类
调度策略通常在下面二者中寻找平衡,通过一套复杂的算法来决定最值得运行的进程投入运行
IO消耗型:进程大部分时间用来提交或等待IO请求
处理器消耗性:进程大部分时间用在执行代码
2、进程优先级
根据进程的价值和其对处理器时间的需求来对进程进行分级,优先级高的先运行,相同优先级轮转运行。Linux采用了两种优先级范围
Nice值:[-20,19],值越大,优先级越低(对其他进程越优待)
实时优先级:[0,99],值越大,优先级高
3、时间片
时间片表示进程在被抢占前所能持续运行的时间
三、调度算法
1、简要介绍
不同任务对系统的重要性不同,如有些任务是用于控制负载均衡、管理调度功能的,因此其不应该被抢占,而像IDLE进程仅仅是当CPU上无运行任务时,用于管理CPU空闲状态的,故其不应该抢占其他进程,为了更好的满足这些任务调度需求,内核实现了不同策略的调度器类
在Linux中调度器是以module方式提供的,在switching-sched.rst文档中可以查看设备默认调度器和切换调度器的方法
# cat /sys/block/sda/queue/scheduler //查看支持的调度器,[]中是当前使用的
[mq-deadline] none
# echo none > /sys/block/sda/queue/scheduler
# cat /sys/block/sda/queue/scheduler
[none] mq-deadline
2、内核支持调度器类
调度器类 | 描述 |
stop | 具有最高的优先级,能抢占所有其他的进程,且不会被其他进程抢占 |
deadline | 优先级介于stop和rt之前,主要用于那些在每个给定周期内,都需要在设定的截止期限之前被调度的任务 |
rt | 优先级低于deadline,高于cfs,主要用于实时性要求高的任务,严格按照优先级调度,对于相同优先级有两种调度策略:FIFO:这种任务被调度后,只能被比它更高优先级的任务抢占,其他任务只能等待;rt:采用时间片轮转的方式调度 |
cfs | 采用完全公平调度算法,依赖虚拟CPU时间,CFS调度器采用红黑树来实现O(logn)复杂度的pick-next算法(红黑树是自平衡二叉搜索树的数据结构,保证了高效的搜索、插入和删除操作,虚拟运行时间最小的进程放到最左的叶子节点) |
idle | idle进程具有最低优先级,当CPU上无其他可调度进程时,才会调度该进程运行 |
四、上下文切换和抢占
上下文切换时一个可执行进程切换到另一个可执行进程,核心是context_switch函数,简化版本如下
static inline void context_switch(struct task_struct *next)
{
// 保存当前进程的CPU上下文,保存如CPU状态等
save_current_context();
// 更新全局变量current和next
current = next;
// 内存屏蔽
barrier();
// 恢复下一个进程的CPU上下文,恢复其CPU状态等
restore_context(next);
}
1、用户抢占
用户抢占发生在:
(1)从系统调用返回用户空间时
(2)从中断处理程序返回用户空间时
2、内核抢占
在Linux中,只要重新调度是安全的(没有持有锁),内核就可以在任何时间抢占正在执行的任务
(1)中断处理程序正在执行,且返回内核空间之前
(2)内核代码再一次具有可抢占性的时候
(3)内核中任务显式调用schedule()
(4)内核中任务阻塞(调用schedule())
【参考博客】
[1] Linux内核设计与实现
[2] https://hqber.com/archives/394/
[3] https://www.cnblogs.com/m00nflower/p/16091642.html
[4] https://zhuanlan.zhihu.com/p/533319895