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,每次选择虚拟时间最小的进程运行,也就是红黑树最左边的进程运行;
  • 相同的物理时间,权重越小的进程,其虚拟时间过得越快;进而体现了进程的优先级;(物理时间越长的进程,实际运行也是更长的;虽然虚拟时间相同,但是其虚拟时间过得更慢;)
posted @ 2024-12-14 18:58  _xingxing  阅读(32)  评论(0)    收藏  举报