本文基于linux-5.4.24分析,由于涉及较多代码的讲解,建议结合源码阅读。另外,浏览本文前,建议先阅读公众号的负载均衡系列文章第一篇:深入解析CFS任务的负载均衡(框架篇)。当然,部分已经提及的基本概念,在本文中也会进行简单回顾。
一、任务放置场景
1. 什么是任务放置(task placement)
linux内核为每个CPU都配置一个cpu runqueue,用以维护当前CPU需要运行的所有线程,调度器会按一定的规则从runqueue中获取某个线程来执行。如果一个线程正挂在某个CPU的runqueue上,此时它处于就绪状态,尚未得到cpu资源,调度器会适时地通过负载均衡(load balance)来调整任务的分布;当它从runqueue中取出并开始执行时,便处于运行状态,若该状态下的任务负载不是当前CPU所能承受的,那么调度器会将其标记为misfit task,周期性地触发主动迁移(active upmigration),将misfit task布置到更高算力的CPU。
上面提到的场景,都是线程已经被分配到某个具体的CPU并且具备有效的负载。如果一个任务线程还未被放置到任何一个CPU上,即处于阻塞状态,又或者它是刚创建、刚开始执行的,此时调度器又是何如做均衡分布的呢?这便是今天我们要花点篇幅来介绍的任务放置场景。
内核中,task placement场景发生在以下三种情况:
(1)进程通过fork创建子进程;
(2)进程通过sched_exec开始执行;
(3)阻塞的进程被唤醒。
2. 调度域(sched domain)及其标志位(sd flag)
如果你正在使用智能手机阅读本文,那你或许知道,目前的手机设备往往具备架构不同的8个CPU core。我们仍然以4小核+4大核的处理器结构为例进行说明。4个小核(cpu0-3)组成一个little cluster,另外4个大核(cpu4-7)组成big cluster,每个cluster的CPU架构相同,它们之间使用同一个调频策略,并且频率调节保持一致。大核相对小核而言,具备更高的算力,但也会带来更多的能量损耗。
对于多处理器均衡(multiprocessor balancing)而言,sched domain是极为重要的概念。内核中以结构体struct sched_domain对其进行定义,将CPU core从下往上按层级划分,对系统所有CPU core进行管理,本系列文章第一篇已进行过较为详细的描述。little cluster和big cluster各自组成底层的MC domain,包含各自cluster的4个CPU core,顶层的DIE domian则覆盖系统中所有的CPU core。
内核调度器依赖sched domain进行均衡,为了方便地对各种均衡状态进行识别,内核定义了一组sched domain flag,用来标识当前sched domain具备的均衡属性。表中,我们可以看到task placement场景常见的三种情况对应的flag。
在构建CPU拓扑结构时,会为各个sched domain配置初始的标识位,如果是异构系统,会设置SD_BALANCE_WAKE: