内核版本:linux-5.15.140 涉及文件:kernel/sched/fair.c、kernel/sched/sched.h
前言
第 37 篇拆完了 load_balance() 主流程:should_we_balance() 确认本 CPU 是组内代表后,第一件正事就是调用 find_busiest_group() 找出该调度域里最忙的组、并算出该搬多少负载。这个函数是整个负载均衡的"判据核心",但它的实现横跨近 500 行:先是统计(update_sd_lb_stats → update_sg_lb_stats),再是选组(update_sd_pick_busiest),然后是判定(find_busiest_group 本体)与定量(calculate_imbalance),最后才是选队列(find_busiest_queue)。
一篇塞不下,拆成两篇。本篇先拆前半场——统计与选组:
load_balance() kernel/sched/fair.c:10469
└── find_busiest_group() kernel/sched/fair.c:9805
└── update_sd_lb_stats() kernel/sched/fair.c:9529 逐组遍历、汇总域级统计
├── update_group_capacity() kernel/sched/fair.c:8705 本地组:刷新组容量
├── update_sg_lb_stats() kernel/sched/fair.c:8902 单组统计
│ └── group_classify() kernel/sched/fair.c:8873 组类型分级
└── update_sd_pick_busiest() kernel/sched/fair.c:8987 挑最忙组
find_busiest_group() 本体与 calculate_imbalance()、find_busiest_queue() 留给下篇。本篇所有结构体与函数均出自上述源码位置,逐段贴原文。
一、两份统计账本:sg_lb_stats 与 sd_lb_stats
负载均衡的判定不直接读 rq,而是先把散落在各 CPU 上的 PELT 信号(见第 34 篇)聚合成两级统计账本:
/*
* sg_lb_stats – stats of a sched_group required for load_balancing
*/
struct sg_lb_stats {
unsigned long avg_load; /*Avg load across the CPUs of the group */
unsigned long group_load; /* Total load over the CPUs of the group */
unsigned long group_capacity;
unsigned long group_util; /* Total utilization over the CPUs of the group */
unsigned long group_runnable; /* Total runnable time over the CPUs of the group */
unsigned int sum_nr_running; /* Nr of tasks running in the group */
unsigned int sum_h_nr_running; /* Nr of CFS tasks running in the group */
unsigned int idle_cpus;
unsigned int group_weight;
enum group_type group_type;
unsigned int group_asym_packing; /* Tasks should be moved to preferred CPU */
unsigned long group_misfit_task_load; /* A CPU has a task too big for its capacity */
#ifdef CONFIG_NUMA_BALANCING
unsigned int nr_numa_running;
unsigned int nr_preferred_running;
#endif
};
kernel/sched/fair.c:8535
sg_lb_stats 是组级账本(一组 CPU 的合计):group_load/group_util/group_runnable 分别是三路 PELT 信号(load、util、runnable,见第 34 篇 sched_avg)在全组 CPU 上的求和;sum_nr_running 数所有任务,sum_h_nr_running 只数 CFS 任务(h_nr_running 沿层级传播,见第 35 篇);idle_cpus 数空闲 CPU;group_capacity 与 group_weight 来自第 36 篇见过的 sched_group_capacity。最关键的是 group_type——本篇第二节的主角。
/*
* sd_lb_stats – Structure to store the statistics of a sched_domain
* during load balancing.
*/
struct sd_lb_stats {
struct sched_group *busiest;/* Busiest group in this sd */
struct sched_group *local;/* Local group in this sd */
unsigned long total_load;/* Total load of all groups in sd */
unsigned long total_capacity;/* Total capacity of all groups in sd */
unsigned long avg_load;/* Average load across all groups in sd */
unsigned int prefer_sibling; /* tasks should go to sibling first */
struct sg_lb_stats busiest_stat;/* Statistics of the busiest group */
struct sg_lb_stats local_stat;/* Statistics of the local group */
};
kernel/sched/fair.c:8558
sd_lb_stats 是域级账本:在所有组的合计之上,重点记两份组级统计——本地组(env->dst_cpu 所在组)与最忙组。注意 busiest_stat/local_stat 是内嵌的 sg_lb_stats 而非指针,所以初始化函数要特别小心:
static inline void init_sd_lb_stats(struct sd_lb_stats *sds)
{
/*
* Skimp on the clearing to avoid duplicate work. We can avoid clearing
* local_stat because update_sg_lb_stats() does a full clear/assignment.
* We must however set busiest_stat::group_type and
* busiest_stat::idle_cpus to the worst busiest group because
* update_sd_pick_busiest() reads these before assignment.
*/
*sds = (struct sd_lb_stats){
.busiest = NULL,
.local = NULL,
.total_load = 0UL,
.total_capacity = 0UL,
.busiest_stat = {
.idle_cpus = UINT_MAX,
.group_type = group_has_spare,
},
};
}
kernel/sched/fair.c:8570
两个初值值得玩味:idle_cpus = UINT_MAX 让任何真实的空闲数都"更小",配合第五节 update_sd_pick_busiest() 中 group_has_spare 分支的 sgs->idle_cpus > busiest->idle_cpus 比较——第一个候选组必然胜出;group_type = group_has_spare 是最低优先级,任何候选组的类型都 ≥ 它。注释也说明了为什么 local_stat 不用清:update_sg_lb_stats() 开头有 memset(sgs, 0, sizeof(*sgs)) 全清。
二、组的六级画像:enum group_type 与 group_classify
统计账本里最要紧的字段是 group_type。内核给每个组打六级标签,枚举值越小优先级越低,选最忙组时直接比大小即可:
/*
* 'group_type' describes the group of CPUs at the moment of load balancing.
*
* The enum is ordered by pulling priority, with the group with lowest priority
* first so the group_type can simply be compared when selecting the busiest
* group. See update_sd_pick_busiest().
*/
enum group_type {
/* The group has spare capacity that can be used to run more tasks. */
group_has_spare = 0,
/*
* The group is fully used and the tasks don't compete for more CPU
* cycles. Nevertheless, some tasks might wait before running.
*/
group_fully_busy,
/*
* SD_ASYM_CPUCAPACITY only: One task doesn't fit with CPU's capacity
* and must be migrated to a more powerful CPU.
*/
group_misfit_task,
/*
* SD_ASYM_PACKING only: One local CPU with higher capacity is available,
* and the task should be migrated to it instead of running on the
* current CPU.
*/
group_asym_packing,
/*
* The tasks' affinity constraints previously prevented the scheduler
* from balancing the load across the system.
*/
group_imbalanced,
/*
* The CPU is overloaded and can't provide expected CPU cycles to all
* tasks.
*/
group_overloaded
};
kernel/sched/fair.c:7831
六级从好到坏:group_has_spare(有富余算力)→ group_fully_busy(满载但够用)→ group_misfit_task(有任务塞不进小核,仅异构容量域)→ group_asym_packing(任务该搬去更优先的 CPU,仅非对称打包域)→ group_imbalanced(此前因亲和性约束均衡失败,挂了"失衡牌")→ group_overloaded(过载,任务抢不到预期算力)。这个顺序就是"值得拉取"的优先级。
打标签的是 group_classify(),判定顺序即优先级从高到低:
static inline enum
group_type group_classify(unsigned int imbalance_pct,
struct sched_group *group,
struct sg_lb_stats *sgs)
{
if (group_is_overloaded(imbalance_pct, sgs))
return group_overloaded;
if (sg_imbalanced(group))
return group_imbalanced;
if (sgs->group_asym_packing)
return group_asym_packing;
if (sgs->group_misfit_task_load)
return group_misfit_task;
if (!group_has_capacity(imbalance_pct, sgs))
return group_fully_busy;
return group_has_spare;
}
kernel/sched/fair.c:8873
其中过载与有余量的判定是镜像的一对:
static inline int sg_imbalanced(struct sched_group *group)
{
return group->sgc->imbalance;
}
kernel/sched/fair.c:8813
/*
* group_has_capacity returns true if the group has spare capacity that could
* be used by some tasks.
* We consider that a group has spare capacity if the * number of task is
* smaller than the number of CPUs or if the utilization is lower than the
* available capacity for CFS tasks.
* For the latter, we use a threshold to stabilize the state, to take into
* account the variance of the tasks' load and to return true if the available
* capacity in meaningful for the load balancer.
* As an example, an available capacity of 1% can appear but it doesn't make
* sense for load balancer because it's in the margin of the utilization
*
*/
static inline bool
group_has_capacity(unsigned int imbalance_pct, struct sg_lb_stats *sgs)
{
if (sgs->sum_nr_running < sgs->group_weight)
return true;
if ((sgs->group_capacity * imbalance_pct) <
(sgs->group_runnable * 100))
return false;
if ((sgs->group_capacity * 100) >
(sgs->group_util * imbalance_pct))
return true;
return false;
}
/*
* group_is_overloaded returns true if the group has enough tasks on the
* cpus but still can't provide expected CPU cycles for each of them.
* We consider that a group is overloaded when the sum of the utilization
* of the CPUs is greater than the capacity of the group or the sum of the
* runnable of the CPUs is greater than the capacity of the group
*/
static inline bool
group_is_overloaded(unsigned int imbalance_pct, struct sg_lb_stats *sgs)
{
if (sgs->sum_nr_running <= sgs->group_weight)
return false;
if ((sgs->group_capacity * 100) <
(sgs->group_util * imbalance_pct))
return true;
if ((sgs->group_capacity * imbalance_pct) <
(sgs->group_runnable * 100))
return true;
return false;
}
kernel/sched/fair.c:8818~8870`
两个函数都遵循同一套三层判定,只是比较方向相反:
imbalance_pct 制造的滞回区很关键:util 在容量的 ±17% 区间内时既不算过载也不算有余量——group_is_overloaded 返回 false、group_has_capacity 也返回 false,最终落进 group_fully_busy。这防止了边界负载在两个状态间抖动,导致均衡器来回搬任务。注释里那句"an available capacity of 1% can appear but it doesn’t make sense"说的就是:容量余 1% 不值得拉任务过来。
sg_imbalanced 读的是 group->sgc->imbalance——第 37 篇 LBF_SOME_PINNED 善后时在父域组上立的"失衡牌"(sgc->imbalance = 1),下篇 find_busiest_group() 见到这个类型会直接 force_balance 绕过常规检查。
三、update_sg_lb_stats:单组逐 CPU 统计
static inline void update_sg_lb_stats(struct lb_env *env,
struct sched_group *group,
struct sg_lb_stats *sgs,
int *sg_status)
{
int i, nr_running, local_group;
memset(sgs, 0, sizeof(*sgs));
local_group = cpumask_test_cpu(env->dst_cpu, sched_group_span(group));
for_each_cpu_and(i, sched_group_span(group), env->cpus) {
struct rq *rq = cpu_rq(i);
sgs->group_load += cpu_load(rq);
sgs->group_util += cpu_util(i);
sgs->group_runnable += cpu_runnable(rq);
sgs->sum_h_nr_running += rq->cfs.h_nr_running;
nr_running = rq->nr_running;
sgs->sum_nr_running += nr_running;
if (nr_running > 1)
*sg_status |= SG_OVERLOAD;
if (cpu_overutilized(i))
*sg_status |= SG_OVERUTILIZED;
#ifdef CONFIG_NUMA_BALANCING
sgs->nr_numa_running += rq->nr_numa_running;
sgs->nr_preferred_running += rq->nr_preferred_running;
#endif
/*
* No need to call idle_cpu() if nr_running is not 0
*/
if (!nr_running && idle_cpu(i)) {
sgs->idle_cpus++;
/* Idle cpu can't have misfit task */
continue;
}
if (local_group)
continue;
/* Check for a misfit task on the cpu */
if (env->sd->flags & SD_ASYM_CPUCAPACITY &&
sgs->group_misfit_task_load < rq->misfit_task_load) {
sgs->group_misfit_task_load = rq->misfit_task_load;
*sg_status |= SG_OVERLOAD;
}
}
/* Check if dst CPU is idle and preferred to this group */
if (env->sd->flags & SD_ASYM_PACKING &&
env->idle != CPU_NOT_IDLE &&
sgs->sum_h_nr_running &&
sched_asym_prefer(env->dst_cpu, group->asym_prefer_cpu)) {
sgs->group_asym_packing = 1;
}
sgs->group_capacity = group->sgc->capacity;
sgs->group_weight = group->group_weight;
sgs->group_type = group_classify(env->sd->imbalance_pct, group, sgs);
/* Computing avg_load makes sense only when group is overloaded */
if (sgs->group_type == group_overloaded)
sgs->avg_load = (sgs->group_load * SCHED_CAPACITY_SCALE) /
sgs->group_capacity;
}
kernel/sched/fair.c:8902
逐段拆:
1. 遍历范围:for_each_cpu_and(i, sched_group_span(group), env->cpus) 取组 span 与均衡候选 CPU 集合的交集——env->cpus 在第 37 篇里已被 LBF_ALL_PINNED 善后逐轮剔除搬不动的 CPU。
2. 三路信号累加:cpu_load()、cpu_runnable() 直接读 cfs_rq 的 PELT 均值:
static unsigned long cpu_load(struct rq *rq)
{
return cfs_rq_load_avg(&rq->cfs);
}
kernel/sched/fair.c:5989
static unsigned long cpu_runnable(struct rq *rq)
{
return cfs_rq_runnable_avg(&rq->cfs);
}
kernel/sched/fair.c:6025
cpu_util() 略复杂(fair.c:6742),取 cfs_rq->avg.util_avg 与"当前 runnable 任务的估计利用率之和"的较大值——刚睡醒的大任务不会因为衰减中的 util 而被低估,这是 EAS/DVFS 的需求,此处不展开。
3. 两个状态位:sg_status 是本域统计期间的聚合标志,nr_running > 1 置 SG_OVERLOAD,任一 CPU cpu_overutilized() 置 SG_OVERUTILIZED。这两个宏定义在:
#define SG_OVERLOAD0x1 /* More than one runnable task on a CPU. */
#define SG_OVERUTILIZED0x2 /* One or more CPUs are over-utilized. */
kernel/sched/sched.h:785
它们不是组统计,而是回写给 root_domain 的全局指示器(第四节),供唤醒选核路径(check_preempt_wakeup 之后的 select_task_rq_fair)快速判断系统是否过载。
4. 空闲计数:!nr_running && idle_cpu(i) 才计入 idle_cpus——nr_running 为 0 是必要条件,idle_cpu() 再确认 rq->curr 是 idle 且无待处理唤醒,避免把刚要跑任务的 CPU 误判为空闲。注释"Idle cpu can’t have misfit task"顺带 continue 掉了后面的 misfit 检查。
5. misfit 检查只对非本地组做:local_group 时直接 continue。misfit 记录全组最大的 rq->misfit_task_load——本地组不需要知道自己的 misfit(均衡方向是把任务从最忙组拉到本地)。
6. asym_packing 特判:目的 CPU 空闲且比本组首选 CPU(group->asym_prefer_cpu,第 36 篇拓扑构建时算好)更受偏爱时,标记 group_asym_packing = 1,group_classify() 据此返回 group_asym_packing 类型。
7. 收尾三级跳:抄入组容量与组权重 → group_classify() 定型 → 仅当过载时计算 avg_load = group_load * 1024 / group_capacity。注释点明:avg_load 只在过载比较时有意义(第五节 group_overloaded 分支拿它 tie-break),非过载组不算除法,省下一次开销。
四、update_sd_lb_stats:遍历组环、汇总域账本
static inline void update_sd_lb_stats(struct lb_env *env, struct sd_lb_stats *sds)
{
struct sched_domain *child = env->sd->child;
struct sched_group *sg = env->sd->groups;
struct sg_lb_stats *local = &sds->local_stat;
struct sg_lb_stats tmp_sgs;
unsigned long sum_util = 0;
int sg_status = 0;
do {
struct sg_lb_stats *sgs = &tmp_sgs;
int local_group;
local_group = cpumask_test_cpu(env->dst_cpu, sched_group_span(sg));
if (local_group) {
sds->local = sg;
sgs = local;
if (env->idle != CPU_NEWLY_IDLE ||
time_after_eq(jiffies, sg->sgc->next_update))
update_group_capacity(env->sd, env->dst_cpu);
}
update_sg_lb_stats(env, sg, sgs, &sg_status);
if (local_group)
goto next_group;
if (update_sd_pick_busiest(env, sds, sg, sgs)) {
sds->busiest = sg;
sds->busiest_stat = *sgs;
}
next_group:
/* Now, start updating sd_lb_stats */
sds->total_load += sgs->group_load;
sds->total_capacity += sgs->group_capacity;
sum_util += sgs->group_util;
sg = sg->next;
} while (sg != env->sd->groups);
/* Tag domain that child domain prefers tasks go to siblings first */
sds->prefer_sibling = child && child->flags & SD_PREFER_SIBLING;
if (env->sd->flags & SD_NUMA)
env->fbq_type = fbq_classify_group(&sds->busiest_stat);
if (!env->sd->parent) {
struct root_domain *rd = env->dst_rq->rd;
/* update overload indicator if we are at root domain */
WRITE_ONCE(rd->overload, sg_status & SG_OVERLOAD);
/* Update over-utilization (tipping point, U >= 0) indicator */
WRITE_ONCE(rd->overutilized, sg_status & SG_OVERUTILIZED);
trace_sched_overutilized_tp(rd, sg_status & SG_OVERUTILIZED);
} else if (sg_status & SG_OVERUTILIZED) {
struct root_domain *rd = env->dst_rq->rd;
WRITE_ONCE(rd->overutilized, SG_OVERUTILIZED);
trace_sched_overutilized_tp(rd, SG_OVERUTILIZED);
}
update_idle_cpu_scan(env, sum_util);
}
kernel/sched/fair.c:9529
1. do-while 走组环:sg = sg->next 直到回到 env->sd->groups——第 36 篇建的环形链表在这里派上用场,遍历不需要额外容器。
2. 本地组特殊处理:判到 env->dst_cpu 在组 span 内,记下 sds->local,且统计直接写进 sds->local_stat(不用 tmp 再拷贝)。同时趁机刷新组容量:update_group_capacity()(第 36 篇已拆)沿子域把各 CPU 当前容量(扣掉 RT/DL/irq 占用后)累进 sgc->capacity。刷新有节流——CPU_NEWLY_IDLE 均衡太频繁,须等 sgc->next_update 到点(周期均衡与 newidle 均衡的取舍)。
3. 非本地组交给 update_sd_pick_busiest() 竞选最忙组,胜出则整体拷入 sds->busiest_stat = *sgs(结构体赋值,这也是第一节说内嵌而非指针的原因)。
4. 域级汇总:无论本地与否,total_load、total_capacity、sum_util 一路累加——下篇 find_busiest_group() 的"域平均负载" sds->avg_load 就从这两个 total 来。
5. prefer_sibling 标记:子域带 SD_PREFER_SIBLING(典型:MC 层希望 LLC 尽量空)时,本层均衡倾向于把任务摊到兄弟组,下篇 calculate_imbalance() 会用它切换到按任务数对半分。
6. NUMA 场景的 fbq 分类:fbq_classify_group() 按 NUMA 任务占比把最忙组分为 regular/remote/all 三类,写进 env->fbq_type,供 find_busiest_queue() 跳过"放错了节点"的队列(本篇不展开)。
7. 回写 root_domain 指示器:rd->overload(有 CPU 上多于一个 runnable 任务)只在根域层(!env->sd->parent)写,且是全量赋值——每轮周期均衡都会重新采样;rd->overutilized 则在根域全量写、在非根层只置位不清零(else if 分支)。overutilized 是 EAS(能量感知调度)的总开关信号:一旦任何一个域置位,整个系统退回常规负载均衡。
8. update_idle_cpu_scan 收尾:这个函数(fair.c:9452)用一条抛物线把 LLC 域的 nr_idle_scan 提示刷新掉——当 LLC 利用率升高时按 y = 1024 – x²·pct²/(10000·1024) 缩减唤醒路径 select_idle_cpu() 的扫描 CPU 数,利用率到 85%(即 imbalance_pct=117 的倒数阈值)时归零停止扫描。仅周期均衡时写(newidle 太频繁,写共享 cache line 代价高),且需开 SIS_UTIL 特性。
五、update_sd_pick_busiest:竞选最忙组
static bool update_sd_pick_busiest(struct lb_env *env,
struct sd_lb_stats *sds,
struct sched_group *sg,
struct sg_lb_stats *sgs)
{
struct sg_lb_stats *busiest = &sds->busiest_stat;
/* Make sure that there is at least one task to pull */
if (!sgs->sum_h_nr_running)
return false;
/*
* Don't try to pull misfit tasks we can't help.
* We can use max_capacity here as reduction in capacity on some
* CPUs in the group should either be possible to resolve
* internally or be covered by avg_load imbalance (eventually).
*/
if (sgs->group_type == group_misfit_task &&
(!capacity_greater(capacity_of(env->dst_cpu), sg->sgc->max_capacity) ||
sds->local_stat.group_type != group_has_spare))
return false;
if (sgs->group_type > busiest->group_type)
return true;
if (sgs->group_type < busiest->group_type)
return false;
/*
* The candidate and the current busiest group are the same type of
* group. Let check which one is the busiest according to the type.
*/
switch (sgs->group_type) {
case group_overloaded:
/* Select the overloaded group with highest avg_load. */
if (sgs->avg_load <= busiest->avg_load)
return false;
break;
case group_imbalanced:
/*
* Select the 1st imbalanced group as we don't have any way to
* choose one more than another.
*/
return false;
case group_asym_packing:
/* Prefer to move from lowest priority CPU's work */
if (sched_asym_prefer(sg->asym_prefer_cpu, sds->busiest->asym_prefer_cpu))
return false;
break;
case group_misfit_task:
/*
* If we have more than one misfit sg go with the biggest
* misfit.
*/
if (sgs->group_misfit_task_load < busiest->group_misfit_task_load)
return false;
break;
case group_fully_busy:
/*
* Select the fully busy group with highest avg_load. In
* theory, there is no need to pull task from such kind of
* group because tasks have all compute capacity that they need
* but we can still improve the overall throughput by reducing
* contention when accessing shared HW resources.
*
* XXX for now avg_load is not computed and always 0 so we
* select the 1st one.
*/
if (sgs->avg_load <= busiest->avg_load)
return false;
break;
case group_has_spare:
/*
* Select not overloaded group with lowest number of idle cpus
* and highest number of running tasks. We could also compare
* the spare capacity which is more stable but it can end up
* that the group has less spare capacity but finally more idle
* CPUs which means less opportunity to pull tasks.
*/
if (sgs->idle_cpus > busiest->idle_cpus)
return false;
else if ((sgs->idle_cpus == busiest->idle_cpus) &&
(sgs->sum_nr_running <= busiest->sum_nr_running))
return false;
break;
}
/*
* Candidate sg has no more than one task per CPU and has higher
* per-CPU capacity. Migrating tasks to less capable CPUs may harm
* throughput. Maximize throughput, power/energy consequences are not
* considered.
*/
if ((env->sd->flags & SD_ASYM_CPUCAPACITY) &&
(sgs->group_type <= group_fully_busy) &&
(capacity_greater(sg->sgc->min_capacity, capacity_of(env->dst_cpu))))
return false;
return true;
}
kernel/sched/fair.c:8987
这是本篇最精彩的函数,竞选规则分四层:
第 1 层:一票否决。sum_h_nr_running == 0(组里没有 CFS 任务)直接出局。misfit 类型也有一票否决:本地 CPU 容量不比该组最大容量明显更大(capacity_greater 宏,fair.c:122,(cap1)*1024 > (cap2)*1078,即 cap1 须超出约 5% 才算"更大"),或本地组自己都没有富余——拉过来也解决不了 misfit,白搬。
第 2 层:类型比大小。group_type 枚举值大的直接胜出(过载 > 失衡牌 > ……),这正是第一节注释"ordered by pulling priority"的用意——选最忙组退化成一次整数比较。
第 3 层:同型 tie-break,每种类型有自己的"更忙"标准:
- group_overloaded:比 avg_load(第三节里只在过载时才计算的除法结果);
- group_imbalanced:拿第一个(没有可比性);
- group_asym_packing:比谁的 asym_prefer_cpu 优先级更低(更低优先 CPU 上的活更该搬走);
- group_misfit_task:比最大的 misfit 负载;
- group_fully_busy:注释坦诚说理——满载组任务算力本来就够,理论上不用拉,但拉一点能减少共享硬件资源(cache)争用,提升吞吐;不过 avg_load 此刻恒为 0(第三节只在 overloaded 时算),所以实际选第一个,注释里留着 XXX 标记;
- group_has_spare:比 idle_cpus 少的(空闲 CPU 少 = 更"满"),平手再比 sum_nr_running 多的。注释解释了为什么不用更稳定的"富余容量"比:容量小但空闲 CPU 多的组,反而更不值得从它拉任务——均衡的本质是消灭空闲 CPU。
第 4 层:吞吐保护。异构容量域上,候选组虽然只有"每 CPU 至多一个任务"的轻度负载,但其最小容量 CPU 都比本地 CPU 强 5% 以上时,把任务拉到弱 CPU 上会损害吞吐——直接否决。注释明确"Maximize throughput, power/energy consequences are not considered"。
小结
统计与选组完成后,sds 里已经躺着完整的候选名单。下篇进入 find_busiest_group() 本体:六种本地×最忙类型组合的决策矩阵、calculate_imbalance() 怎么把"该不该搬"翻译成"搬多少"(migrate_load/util/task/misfit 四种模式),以及 find_busiest_queue() 在最忙组内挑出具体源队列。
系列回链:load_balance 主流程与 lb_env 见[第 37 篇];sched_group/sched_group_capacity 结构与组环链见[第 36 篇];PELT 三路信号(load/util/runnable)见[第 34 篇];h_nr_running 层级传播与 removed 机制见[第 35 篇];sched_asym_prefer 与 asym_prefer_cpu 见[第 36 篇]。
网硕互联帮助中心


评论前必须登录!
注册