Linux进程调度内核实现分析
2017-01-08 11:11
411 查看
本文根据两篇博客总结,它们的地址分别是:
http://blog.csdn.net/giantpoplar/article/details/51791002
http://blog.csdn.net/giantpoplar/article/details/51791002
所谓抢占
什么是调度
调度实现原理
进程的优先级
时间片
调度的实现原理
Linux上调度实现的方法
上下文切换
调用schedule的时机
抢占时机
实时调度
调度程序决定进程何时停止,为其他进程腾出 CPU 资源,这种强制挂起行为就是抢占。进程被抢占前运行时间为提前设定好的时间片。
非抢占
进程占据 CPU 资源不放,除非自己主动调用 schedule() 函数,这称为进程让步。
调度程序的功能有:
负责让那些进程运行,那些进程等待
决定每个进程运行多长时间
此外为了更好地用户体验,运行中的进程还可以立即被其他更紧急的进程打断。
总之调度是一个平衡的过程。一方面,它要保证各个运行的的进程能够最大限度使用CPU(即尽量少的切换进程),另一方面,保证各个进程公平使用CPU(即防止一个进程长时间独占CPU的情况)
nice 值的范围是 -20~+19,值越大优先级越低,也就是说 nice 值为-20的进程优先级最大。
实时优先级的范围是 0~99,与 nice 值的定义相反,实时优先级是值越大优先级越高。
实时进程都是一些对响应时间要求比较高的进程,因此系统中有实时优先级高的进程处于队列的话,它们会抢占一般进程的运行时间。
进程的实时优先级高于 nice 值,在内核中,实时优先级的范围是 0~MAX_RT_PRIO-1。
MAX_RT_PRIO的定义参见 include/linux/sched.h
nice值在内核中的范围是 MAX_RT_PRIO~MAX_RT_PRIO+40 即 MAX_RT_PRIO~MAX_PRIO
一个进程有只能拥有实时优先级和 nice 值其中之一。
于是就有了时间片的概念。时间片表示一个进程抢占前能持续运行的时间。
时间片设定,过大会让系统响应变慢;过小会产生由于进程频繁切换带来的开销。
默认的时间片一般为 10ms。
首先确定每个进程占用多少 CPU 时间,如利用 nice 值进行计算。
让占用 CPU 时间多的先运行。(让列宁同志先走)
运行完后,扣除运行进程的 CPU 时间,接下来继续重复这三步。
CFS 算法在分配给每个进程 CPU 时间时,不是分配给它们一个 CPU 的绝对时间,而是根据进程的优先级分配给它们一个占用 CPU 时间的百分比。
举例:
由上图可知,占据百分比之和就是 100%。所以我的理解是 Linux 下 CFS 算法的主要步骤有:
计算每个进程的 vruntime,通过 update_curr() 函数更新进程的 vruntime。
选择具有最小 vruntime 的进程投入运行。
进程运行完后,更新 vruntime,再回到步骤 2。
vruntime 变量存放进程的虚拟运行时间,该运行时间(花在运行上的时间和)是通过对可运行总数的加权后得出的,单位是 ns。计算权重实际上就是计算占用 CPU 的百分比,通过它得出 vruntime,用来记录一个程序运行了多长时间以及还应运行多久。
时间记账
使用 sched_entity 结构体进行记账,该结构体在 task_struct 中被包含。vruntime 作用上面说过了。
进程选择
使用红黑树存放进程队列,以 vruntime 为键值,每次选择 vruntime 最小的进程。这里有用到了一个 trick,使用 leftmost 缓存了红黑树的最左叶子节点,提高效率(STL也这么干了)。
调度器入口
schedule() 函数调用 pick_next_task() 函数,按照优先级从大到小一次检查每一个调度类,从最高优先级调度类中,选择最高优先级进程。
睡眠和唤醒
睡眠:进程标记为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 状态,从红黑树中移除,放入等待队列,调用 schedule 选择和执行一个其他的进程。
唤醒:通过 wake_up() 函数唤醒,进程标记为 TASK_RUNNING 状态,从等待队列放入红黑树中。
switch_to() 从上一个处理器状态切换到新处理器状态,保存回复栈寄存器信息。
一个优先级高的进程进入可执行状态,try_wake_up 设置为 need_resched。
从内核返回用户空间或从中断返回,内核检查 need_resched。如果已经设置,内核会在继续执行之前调用调度程序
从系统调用返回用户空间。
从中断处理程序返回用户空间。
内核抢占:
中断处理程序正在执行,且返回内核空间之前。
内核代码再一次具备可抢占性的时候(我认为就是出于临界区之外时)。
如果内核中的代码显示调用 schedule()。
如果内核中任务阻塞。
SCHED_RR:带有时间片,实时轮流调度。时间片耗尽,允许同一级别进程抢占。
http://blog.csdn.net/giantpoplar/article/details/51791002
http://blog.csdn.net/giantpoplar/article/details/51791002
目录
目录所谓抢占
什么是调度
调度实现原理
进程的优先级
时间片
调度的实现原理
Linux上调度实现的方法
上下文切换
调用schedule的时机
抢占时机
实时调度
所谓抢占
抢占式调度程序决定进程何时停止,为其他进程腾出 CPU 资源,这种强制挂起行为就是抢占。进程被抢占前运行时间为提前设定好的时间片。
非抢占
进程占据 CPU 资源不放,除非自己主动调用 schedule() 函数,这称为进程让步。
什么是调度
调度的原因:现代操作系统都是多任务的,为了能让操作系统更好的发挥多任务的特性,需要一个管理程序来管理所有任务,这个程序就是调度程序。调度程序的功能有:
负责让那些进程运行,那些进程等待
决定每个进程运行多长时间
此外为了更好地用户体验,运行中的进程还可以立即被其他更紧急的进程打断。
总之调度是一个平衡的过程。一方面,它要保证各个运行的的进程能够最大限度使用CPU(即尽量少的切换进程),另一方面,保证各个进程公平使用CPU(即防止一个进程长时间独占CPU的情况)
调度实现原理
调度主要基于进程优先级和时间片实现。进程的优先级
进程的优先级有两种度量方法,一种是nice值,一种是实时优先级。nice 值的范围是 -20~+19,值越大优先级越低,也就是说 nice 值为-20的进程优先级最大。
实时优先级的范围是 0~99,与 nice 值的定义相反,实时优先级是值越大优先级越高。
实时进程都是一些对响应时间要求比较高的进程,因此系统中有实时优先级高的进程处于队列的话,它们会抢占一般进程的运行时间。
进程的实时优先级高于 nice 值,在内核中,实时优先级的范围是 0~MAX_RT_PRIO-1。
MAX_RT_PRIO的定义参见 include/linux/sched.h
#define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO
nice值在内核中的范围是 MAX_RT_PRIO~MAX_RT_PRIO+40 即 MAX_RT_PRIO~MAX_PRIO
#define MAX_PRIO (MAX_RT_PRIO + 40)
一个进程有只能拥有实时优先级和 nice 值其中之一。
时间片
有了优先级,就可以决定谁先运行了。但是对于调度程序来说,并不是运行一次就结束了,必须知道间隔多久进行下次调度。于是就有了时间片的概念。时间片表示一个进程抢占前能持续运行的时间。
时间片设定,过大会让系统响应变慢;过小会产生由于进程频繁切换带来的开销。
默认的时间片一般为 10ms。
调度的实现原理
基本步骤我的理解是:首先确定每个进程占用多少 CPU 时间,如利用 nice 值进行计算。
让占用 CPU 时间多的先运行。(让列宁同志先走)
运行完后,扣除运行进程的 CPU 时间,接下来继续重复这三步。
Linux上调度实现的方法
Linux 上采用了“完全公平调度算法”,简称“CFS”。CFS 算法在分配给每个进程 CPU 时间时,不是分配给它们一个 CPU 的绝对时间,而是根据进程的优先级分配给它们一个占用 CPU 时间的百分比。
举例:
进程名 | nice值 | 百分比 |
---|---|---|
进程A | 1 | 10% |
进程B | 3 | 30% |
进程C | 6 | 60% |
计算每个进程的 vruntime,通过 update_curr() 函数更新进程的 vruntime。
选择具有最小 vruntime 的进程投入运行。
进程运行完后,更新 vruntime,再回到步骤 2。
vruntime 变量存放进程的虚拟运行时间,该运行时间(花在运行上的时间和)是通过对可运行总数的加权后得出的,单位是 ns。计算权重实际上就是计算占用 CPU 的百分比,通过它得出 vruntime,用来记录一个程序运行了多长时间以及还应运行多久。
时间记账
使用 sched_entity 结构体进行记账,该结构体在 task_struct 中被包含。vruntime 作用上面说过了。
进程选择
使用红黑树存放进程队列,以 vruntime 为键值,每次选择 vruntime 最小的进程。这里有用到了一个 trick,使用 leftmost 缓存了红黑树的最左叶子节点,提高效率(STL也这么干了)。
调度器入口
schedule() 函数调用 pick_next_task() 函数,按照优先级从大到小一次检查每一个调度类,从最高优先级调度类中,选择最高优先级进程。
睡眠和唤醒
睡眠:进程标记为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE 状态,从红黑树中移除,放入等待队列,调用 schedule 选择和执行一个其他的进程。
唤醒:通过 wake_up() 函数唤醒,进程标记为 TASK_RUNNING 状态,从等待队列放入红黑树中。
上下文切换
switch_mm() 函数把虚拟内存从上一个进程切换到新进程。switch_to() 从上一个处理器状态切换到新处理器状态,保存回复栈寄存器信息。
调用schedule()的时机
某个进程应该被抢占,scheduer_tick 设置为 need_resched。一个优先级高的进程进入可执行状态,try_wake_up 设置为 need_resched。
从内核返回用户空间或从中断返回,内核检查 need_resched。如果已经设置,内核会在继续执行之前调用调度程序
抢占时机
用户抢占:从系统调用返回用户空间。
从中断处理程序返回用户空间。
内核抢占:
中断处理程序正在执行,且返回内核空间之前。
内核代码再一次具备可抢占性的时候(我认为就是出于临界区之外时)。
如果内核中的代码显示调用 schedule()。
如果内核中任务阻塞。
实时调度
SCHED_FIFO:不使用时间片,先入先出。只有更高优先级别的SCHED_FIFO,SHED_RR 进程才能抢占,同一级别不能抢占。SCHED_RR:带有时间片,实时轮流调度。时间片耗尽,允许同一级别进程抢占。
相关文章推荐
- Linux2.6 内核进程调度分析
- linux内核进程调度CFS 完全公平调度算法分析(一)
- Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)
- Linux2.6 内核进程调度分析
- Linux进程调度CFS算法实现分析
- Linux2.6 内核进程调度分析 .
- Linux0.11内核--进程调度分析之2.调度
- Linux0.11内核--进程调度分析之1.初始化
- Linux-0.11内核源码分析系列:进程调度
- Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度
- 每日阅读3之内核设计与实现(第三版)4.5——linux调度实现之进程选择
- Linux-0.11内核源码分析系列:进程调度sleep_on()函数分析
- (原创)linux内核进程调度以及定时器实现机制
- Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)
- 【转】Linux内核进程调度以及定时器实现机制
- Linux内核分析——第四章 进程调度
- 读薄「Linux 内核设计与实现」(2) - 进程管理和调度
- Linux2.6内核进程调度核心代码分析
- linux内核进程调度以及定时器实现机制
- Linux2.6 内核进程调度分析