专栏名称: Linux内核之旅
Linux内核之旅
目录
相关文章推荐
Linux内核之旅  ·  eBPF 治理 NFS 存储实践-4 ·  4 天前  
Linux内核之旅  ·  公平CFS调度类:SCHED_NORMAL、 ... ·  2 周前  
Linux内核之旅  ·  公平调度类的延伸:EEVDF ·  1 周前  
Linux内核之旅  ·  调用栈过深导致的火焰图错误该如何解决 ·  5 天前  
51好读  ›  专栏  ›  Linux内核之旅

公平CFS调度类:SCHED_NORMAL、SCHED_BATCH和SCHED_IDLE

Linux内核之旅  · 公众号  · linux  · 2024-12-27 14:18

正文

公平类的调度算法强调的是一个公平,比如有5个任务,则每个人分享20%的CPU。这是一种人人平等的理想情况,实际情况下,nice值会决定任务的权重,nice值位于[-20, 19]之间,nice越低,权重越高,应该获得更多的CPU。nice意味着与人为善,比如坐地铁看到老人或者儿童就让座,这样就是nice,显然nice值越高,自己能坐到座位的机会越低(优先级越低);nice值越低,优先级越高。

在内核的Documentation/scheduler/sched-nice-design.rst中实际画了这样一幅图,在Linux内核早期的O(1)算法里,nice值实际改变了任务能获取的时间片的大小:

现在Linux的CFS调度类采用的是权重影响任务运行的vruntime虚拟运行时间的概率。

负载权重基准,就是nice=0的任务对应的权重 1024,任务权重的计算公式为:

从而得到如下权重表(位于kernel/sched/core.c):

CFS采用红黑树(RBTREE)组织调度实体,追求vruntime的公平,每次都跑vruntime最小的调度实体。

下面一个程序创建2个线程,包括主线程在内,3个线程都跑while(1)。

为了屏蔽负载均衡的影响,我们用taskset把它绑定在CPU0上面跑。

top -H命令看他们的CPU利用率分布,大概是均等的33%:

默认情况下,nice都是0。下面我们把4606 线程renice为-1, 把4608线程 renice为1。

特别留意其中renice -1时候的sudo和renice 1的时候没有sudo,这说明想提升优先级(更多索取)是要权限的,想降低优先级(更多自我奉献)是比较随意的。

重新看CPU利用率的情况,基本上4607的CPU利用率没有变,4606由于提升了权重,CPU利用率变成约为4607的1.25倍;而4608则变地更少,4607约是4608的1.25倍。

CFS的实现依赖于红黑树,试想如果我们用链表而不是红黑树来实现vruntime的排序,插入的开销可能就是O(N)而不是对数复杂度O(log n)

CFS内部有3种调度策略:SCHED_NORMAL、SCHED_BATCH和SCHED_IDLE

  • SCHED_NORMAL:常规任务;

  • SCHED_BATCH:非交互的批处理任务;

  • SCHED_IDLE:低优先级任务

那么它们是需要3个分开的红黑树,还是1个红黑树上面放入所有的3种策略的任务呢?答案实际是后者。

为了实现SCHED_IDLE的所谓“低优先级”,实际上它只要一个很低的权重就可以了,比如:WEIGHT_IDLEPRIO = 3。

fair这个sched_class类的wakeup_preempt() callback中,SCHED_IDLE的任务可以被非idle的抢占:

另外,check_preempt_wakeup_fair()也进一步阻止了batch和idle任务去抢占normal任务(也并阻止了batch内部多个任务、idle内部多个任务的互相抢占):

下面的代码,main里面创建2个线程,加上主线程在内,3个线程都while(1)执行死循环。

三个线程的调度策略和nice值如下:

nice值19的权重等于15,是来自于前面的sched_prio_to_weight表。

编译并执行(绑定在一个CPU0上面跑):

top -H看看3个线程的CPU利用率:

这里明显可以看出,SCHED_NORMAL并不会阻止SCHED_BATCH和SCHED_IDLE的运行,而且由于11697和11698都是权重15,它们的CPU利用率相同(尽管他们的调度策略分别是SCHED_NORMAL和SCHED_BATCH),它们的CPU利用率都是权重为3的SCHED_IDLE策略的11699的5倍。它们这3者主要的区别,主要还是任务醒来时候谁可以抢占谁的问题上(本例中3个线程都是无睡眠的);其他的,还是按照CFS的权重来跑的。我们也可以用chrt来验证一下3个线程的调度策略: