cfs
copy_process
wake_up_new_task
scheduler_tick
__schedule
什么是调度类
struct sched_class, 目的是将调度器模块化;
Linux 有哪些调度类
- dl_sched_class:
- rt_sched_class:
- fair_sched_class:
- idle_sched_class:
- 没太明白调度策略在其中起到的作用是什么;
CFS调度:nice,权重
- CFS调度器和以往的调度器不同之处在于没有时间片的概念,而是根据权重分配cpu使用时间的比例。
- 权重越大分配的时间比例越大,相当于优先级越高。
- 计算如下:分配给进程的时间 = 总的cpu时间 * 进程的权重/就绪队列(runqueue)所有进程权重之和
- nice:CFS调度器针对优先级又提出了nice值的概念,其实和权重是一一对应的关系。
- 可以这样理解吗:优先级对应nice,对应权重;
- nice值和权重有一个对应关系:weight = 1024 / 1.25^nice;
- nice和权重对应的依据是:
进程每降低一个nice值,将多获得10% cpu的时间。
调度延迟(调度周期,sched period)
- 调度延迟就是:所有进程运行一次的时间间隔;
- 当系统处于就绪态的进程少于一个定值(默认值8:
static unsigned int sched_nr_latency = 8;
)的时候,调度延迟也是固定一个值不变(默认值6ms:unsigned int sysctl_sched_latency = 6000000ULL;
) - 当系统就绪态进程个数超过这个值时,我们保证每个进程至少运行一定的时间才让出cpu。这个“至少一定的时间”被称为最小粒度时间:
unsigned int sysctl_sched_min_granularity = 750000ULL;
- 调度周期计算函数是
__sched_period()。
虚拟时间:cfs核心思想
不同权重的进程运行时间是不同的(调度周期 * 权重比例),但CFS想保证每个进程运行时间相等。因此CFS引入了虚拟时间的概念(物理时间不等,但通过一定的转化,保证转化后的虚拟时间相等),也就是说上面的2.7ms和3.3ms经过一个公式的转换可以得到一样的值,这个转换后的值称作虚拟时间。
虚拟时间的计算公式:
NICE_0_LOAD
vriture_runtime = wall_time * ----------------
weight
怎么理解这个式子?比如存在两个进程A,B。他们各自的权重是x,y;调度周期是1
- 那么A进程的: wall_time = x / (x + y);
- 那么B进程的: wall_time = y / (x + y);
- A进程的vriture_runtime: x / (x + y) * NICE_0_LOAD / x = (NICE_0_LOAD / (x + y));
- B进程的vriture_runtime: y / (x + y) * NICE_0_LOAD / y = (NICE_0_LOAD / (x + y));
- 也就是通过这个公式,每个进程的虚拟时间就是: NICE_0_LOAD / (总权重);
inv_weight
NICE_0_LOAD
vriture_runtime = wall_time * ----------------
weight
NICE_0_LOAD * 2^32
= (wall_time * -------------------------) >> 32
weight
2^32
= (wall_time * NICE_0_LOAD * inv_weight) >> 32 (inv_weight = ------------ )
weight
将除法省略掉,将 1/weight 转化为 2^32 / weight,并把这个值进行了保存;
代码计算
static u64 __calc_delta(u64 delta_exec, unsigned long weight, struct load_weight *lw)
return mul_u64_u32_shr(delta_exec, NICE_0_LOAD * inv_weight , WMULT_SHIFT = 32 );
就绪队列:
- 一个调度类并不是直接管理task_struct,而是引入调度实体的概念(struct sched_entity)。
- CFS调度器使用sched_entity跟踪调度信息:
- CFS调度器使用cfs_rq跟踪就绪队列信息以及管理就绪态调度实体,并维护一棵按照虚拟时间排序的红黑树。
struct rb_node run_node; -> struct rb_root_cached tasks_timeline;
CFS 思想总结:
- 物理时间:根据权重占比分配调度周期;
- 虚拟时间:将物理时间根据一个式子转化为虚拟时间,各个调度实体的虚拟时间都相同:1/权重之和 * 调度周期;
- 就绪队列是一个percpu的概念,里面包含cfs的就绪队列;
- 每一个调度实体加入cfs的就绪队列,以vruntime 为 key,每次选择虚拟时间最小的进程运行,也就是红黑树最左边的进程运行;
- 相同的物理时间,权重越小的进程,其虚拟时间过得越快;进而体现了进程的优先级;(物理时间越长的进程,实际运行也是更长的;虽然虚拟时间相同,但是其虚拟时间过得更慢;)