Linux最忙CPU组查找函数和最忙运行队列查找函数

文章目录

一、最忙CPU组查找函数find_busiest_group

c 复制代码
static struct sched_group *
find_busiest_group(struct sched_domain *sd, int this_cpu,
                   unsigned long *imbalance, enum idle_type idle)
{
        struct sched_group *busiest = NULL, *this = NULL, *group = sd->groups;
        unsigned long max_load, avg_load, total_load, this_load, total_pwr;

        max_load = this_load = total_load = total_pwr = 0;

        do {
                unsigned long load;
                int local_group;
                int i, nr_cpus = 0;

                local_group = cpu_isset(this_cpu, group->cpumask);

                avg_load = 0;

                for_each_cpu_mask(i, group->cpumask) {
                        if (local_group)
                                load = target_load(i);
                        else
                                load = source_load(i);

                        nr_cpus++;
                        avg_load += load;
                }

                if (!nr_cpus)
                        goto nextgroup;

                total_load += avg_load;
                total_pwr += group->cpu_power;

                avg_load = (avg_load * SCHED_LOAD_SCALE) / group->cpu_power;

                if (local_group) {
                        this_load = avg_load;
                        this = group;
                        goto nextgroup;
                } else if (avg_load > max_load) {
                        max_load = avg_load;
                        busiest = group;
                }
nextgroup:
                group = group->next;
        } while (group != sd->groups);

        if (!busiest || this_load >= max_load)
                goto out_balanced;

        avg_load = (SCHED_LOAD_SCALE * total_load) / total_pwr;

        if (this_load >= avg_load ||
                        100*max_load <= sd->imbalance_pct*this_load)
                goto out_balanced;

        *imbalance = min(max_load - avg_load, avg_load - this_load);

        *imbalance = (*imbalance * min(busiest->cpu_power, this->cpu_power))
                                / SCHED_LOAD_SCALE;

        if (*imbalance < SCHED_LOAD_SCALE - 1) {
                unsigned long pwr_now = 0, pwr_move = 0;
                unsigned long tmp;

                if (max_load - this_load >= SCHED_LOAD_SCALE*2) {
                        *imbalance = 1;
                        return busiest;
                }


                pwr_now += busiest->cpu_power*min(SCHED_LOAD_SCALE, max_load);
                pwr_now += this->cpu_power*min(SCHED_LOAD_SCALE, this_load);
                pwr_now /= SCHED_LOAD_SCALE;

                tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/busiest->cpu_power;
                if (max_load > tmp)
                        pwr_move += busiest->cpu_power*min(SCHED_LOAD_SCALE,
                                                        max_load - tmp);

                tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/this->cpu_power;
                if (max_load < tmp)
                        tmp = max_load;
                pwr_move += this->cpu_power*min(SCHED_LOAD_SCALE, this_load + tmp);
                pwr_move /= SCHED_LOAD_SCALE;

                if (pwr_move < pwr_now + SCHED_LOAD_SCALE / 8)
                        goto out_balanced;

                *imbalance = 1;
                return busiest;
        }

        *imbalance = (*imbalance + 1) / SCHED_LOAD_SCALE;

        return busiest;

out_balanced:
        if (busiest && (idle == NEWLY_IDLE ||
                        (idle == SCHED_IDLE && max_load > SCHED_LOAD_SCALE)) ) {
                *imbalance = 1;
                return busiest;
        }

        *imbalance = 0;
        return NULL;
}

这个函数是Linux内核负载均衡器的核心,负责在调度域中找到最繁忙的CPU组,并计算需要迁移的任务量

1. 函数原型和参数

c 复制代码
static struct sched_group *
find_busiest_group(struct sched_domain *sd, int this_cpu,
                   unsigned long *imbalance, enum idle_type idle)

参数

  • sd:当前调度域
  • this_cpu:当前CPU编号
  • imbalance:输出参数,需要迁移的负载量
  • idle:当前CPU的空闲状态

返回值:最忙的调度组,NULL表示无需负载均衡

2. 变量初始化

c 复制代码
struct sched_group *busiest = NULL, *this = NULL, *group = sd->groups;
unsigned long max_load, avg_load, total_load, this_load, total_pwr;

max_load = this_load = total_load = total_pwr = 0;

关键变量

  • busiest:指向最忙的调度组
  • this:指向当前CPU所在的调度组
  • max_load:最忙组的负载
  • this_load:当前组的负载
  • total_load:所有组的总负载
  • total_pwr:所有组的总计算能力

3. 调度组遍历和负载统计

c 复制代码
do {
    unsigned long load;
    int local_group;
    int i, nr_cpus = 0;

    local_group = cpu_isset(this_cpu, group->cpumask);

本地组判断

c 复制代码
local_group = cpu_isset(this_cpu, group->cpumask);
  • 检查当前CPU是否属于这个调度组
  • 本地组和远程组采用不同的负载计算策略

3.1.组内CPU负载累加

c 复制代码
avg_load = 0;
for_each_cpu_mask(i, group->cpumask) {
    if (local_group)
        load = target_load(i);
    else
        load = source_load(i);

    nr_cpus++;
    avg_load += load;
}

for_each_cpu_mask

  • 遍历调度组中所有的CPU
  • group->cpumask:位图,标识该组包含哪些CPU
  • i:循环变量,表示当前CPU编号

3.2.负载计算函数差异

**target_load() **

实现

c 复制代码
static inline unsigned long target_load(int cpu)
{
        runqueue_t *rq = cpu_rq(cpu);
        unsigned long load_now = rq->nr_running * SCHED_LOAD_SCALE;

        return max(rq->cpu_load, load_now);
}

特点:

  • 获取的负载值取 平均负载值瞬时负载值的较大值
  • 瞬时负载值影响较大
  • 得到的负载值大,迁移阈值高

**source_load() **

实现

c 复制代码
static inline unsigned long source_load(int cpu)
{
        runqueue_t *rq = cpu_rq(cpu);
        unsigned long load_now = rq->nr_running * SCHED_LOAD_SCALE;

        return min(rq->cpu_load, load_now);
}

特点:

  • 获取的负载值取 平均负载值瞬时负载值的较小值
  • 瞬时负载值影响较小
  • 得到的负载值小,迁移阈值低

4.负载标准化和power调整

c 复制代码
#define SCHED_LOAD_SCALE        128UL   /* increase resolution of load */

total_load += avg_load;
total_pwr += group->cpu_power;

/* Adjust by relative CPU power of the group */
avg_load = (avg_load * SCHED_LOAD_SCALE) / group->cpu_power;

变量含义

  • total_load:所有调度组的原始负载总和
  • total_pwr:所有调度组的计算能力总和
  • group->cpu_power:该组的相对计算能力
text 复制代码
标准化负载 = (原始负载 × 缩放因子) / CPU计算能力

将不同性能CPU的负载转换为可比较的标准单位

5.最忙组选择

c 复制代码
if (local_group) {
    this_load = avg_load;
    this = group;
    goto nextgroup;
} else if (avg_load > max_load) {
    max_load = avg_load;
    busiest = group;
}

local_group

  • 记录本地组的负载到this_load

avg_load > max_load

  • 找到负载最高的远程组作为busiest

6.检查是否需要平衡

c 复制代码
if (!busiest || this_load >= max_load)
    goto out_balanced;

avg_load = (SCHED_LOAD_SCALE * total_load) / total_pwr;

if (this_load >= avg_load ||
        100*max_load <= sd->imbalance_pct*this_load)
    goto out_balanced;

条件1:!busiest

c 复制代码
!busiest  // 没有找到最忙的组

含义:只有一个本地调度组

条件2:this_load >= max_load

c 复制代码
this_load >= max_load  // 当前组负载 ≥ 最忙组负载

含义:当前CPU所在组的负载已经不低于最忙组的负载

  • 如果当前组已经比最忙组更忙,迁移任务只会让情况更糟

全局平均负载计算

c 复制代码
avg_load = (SCHED_LOAD_SCALE * total_load) / total_pwr;

计算公式

text 复制代码
全局平均负载 = (缩放因子 × 总原始负载) / 总计算能力

计算整个调度域的负载

c 复制代码
if (this_load >= avg_load ||
        100*max_load <= sd->imbalance_pct*this_load)
    goto out_balanced;

条件3:this_load >= avg_load

c 复制代码
this_load >= avg_load  // 当前组负载 ≥ 全局平均负载

含义:当前组已经达到或超过全局平均负载水平

  • 当前组已经"贡献"了应有的负载份额

条件4:100\*max_load <= sd->imbalance_pct\*this_load

c 复制代码
100 * max_load <= sd->imbalance_pct * this_load

重新排列更易理解

c 复制代码
max_load <= (sd->imbalance_pct / 100) * this_load

实际含义

text 复制代码
最忙组负载 ≤ 不平衡百分比 × 当前组负载

典型配置

c 复制代码
sd->imbalance_pct = 125  // 25%的不平衡容忍度

计算示例

text 复制代码
当前组负载 (this_load): 100
最忙组负载 (max_load): 120
不平衡百分比: 125%

计算: 120 <= (125/100) × 100 = 125
条件成立: 120 <= 125 → 跳转到平衡状态

设计原理容忍适度不平衡

  • 允许一定程度的负载差异存在
  • 避免因微小不平衡触发昂贵的迁移操作
  • 在性能和完美平衡间取得权衡

6.1.不执行负载均衡的四种情况

  1. 无候选组!busiest
    • 说明只有一个本地组
  2. 当前组更忙this_load >= max_load
    • 迁移会使情况更糟
  3. 当前组已达平均this_load >= avg_load
    • 当前组已承担公平份额
  4. 不平衡在容忍范围内max_load ≤ 125% × this_load
    • 差异太小,不值得迁移

7.不平衡量计算

c 复制代码
*imbalance = min(max_load - avg_load, avg_load - this_load);

/* How much load to actually move to equalise the imbalance */
*imbalance = (*imbalance * min(busiest->cpu_power, this->cpu_power))
                        / SCHED_LOAD_SCALE;

这段代码计算了需要迁移的负载量

7.1.基础不平衡量计算

c 复制代码
*imbalance = min(max_load - avg_load, avg_load - this_load);

计算两个方向的不平衡量,取较小值:

  1. max_load - avg_load:最忙组超出平均的负载量
  2. avg_load - this_load:当前组低于平均的负载量
7.1.1.为什么取最小值?

防止过度迁移

确保可行性

  • 迁移量不能超过当前组的接收能力
  • 迁移量不能超过最忙组的可释放量

7.2.功率调整计算

c 复制代码
*imbalance = (*imbalance * min(busiest->cpu_power, this->cpu_power))
            / SCHED_LOAD_SCALE;

计算公式

text 复制代码
实际迁移量 = (基础不平衡量 × 最小CPU功率) / 缩放因子

按较弱CPU的能力调整

  • 迁移量受限于两个组中计算能力较低的一方
  • 确保迁移后的负载分布考虑实际硬件能力

7.3.在负载均衡流程中的位置

完整决策链

text 复制代码
1. 找到最忙组 ✓
2. 检查是否需要平衡 ✓  
3. 计算基础不平衡量 ✓
4. 按CPU功率调整 ✓
5. 转换为实际任务数
6. 选择具体任务迁移

8.小不平衡量处理

c 复制代码
	if (*imbalance < SCHED_LOAD_SCALE - 1) {
    			unsigned long pwr_now = 0, pwr_move = 0;
                unsigned long tmp;

                if (max_load - this_load >= SCHED_LOAD_SCALE*2) {
                        *imbalance = 1;
                        return busiest;
                }


                pwr_now += busiest->cpu_power*min(SCHED_LOAD_SCALE, max_load);
                pwr_now += this->cpu_power*min(SCHED_LOAD_SCALE, this_load);
                pwr_now /= SCHED_LOAD_SCALE;

                tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/busiest->cpu_power;
                if (max_load > tmp)
                        pwr_move += busiest->cpu_power*min(SCHED_LOAD_SCALE,
                                                        max_load - tmp);

                tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/this->cpu_power;
                if (max_load < tmp)
                        tmp = max_load;
                pwr_move += this->cpu_power*min(SCHED_LOAD_SCALE, this_load + tmp);
                pwr_move /= SCHED_LOAD_SCALE;

                if (pwr_move < pwr_now + SCHED_LOAD_SCALE / 8)
                        goto out_balanced;

                *imbalance = 1;
	}

这段代码处理小规模不平衡的特殊情况,进行经济性分析以决定是否值得迁移

8.1.条件判断入口

c 复制代码
if (*imbalance < SCHED_LOAD_SCALE - 1) {

含义 :当计算出的不平衡量小于127时(SCHED_LOAD_SCALE=128

设计意图

  • 不平衡量很小,可能不值得迁移
  • 需要进行成本收益分析
  • 避免因微小不平衡触发昂贵的迁移操作

8.2.严重不平衡的特殊处理

c 复制代码
if (max_load - this_load >= SCHED_LOAD_SCALE*2) {
    *imbalance = 1;
    return busiest;
}

条件分析

c 复制代码
max_load - this_load >= 256  // SCHED_LOAD_SCALE*2

含义:最忙组与当前组的负载差异很大(≥256)

特殊处理

  • 即使计算出的*imbalance很小
  • 也强制迁移至少1个任务(*imbalance = 1
  • 立即返回最忙组

设计原理:当负载差异很大时,即使精细计算显示迁移量小,也优先解决明显的不平衡

8.3.当前系统吞吐量计算

c 复制代码
pwr_now += busiest->cpu_power * min(SCHED_LOAD_SCALE, max_load);
pwr_now += this->cpu_power * min(SCHED_LOAD_SCALE, this_load);
pwr_now /= SCHED_LOAD_SCALE;

计算公式

text 复制代码
当前吞吐量 = (busiest_power × min(128, max_load) + this_power × min(128, this_load)) / 128

数学原理

  • 每个CPU的贡献受其计算能力和负载限制
  • min(SCHED_LOAD_SCALE, load):负载上限为128
  • 除以SCHED_LOAD_SCALE得到标准化吞吐量

8.4.迁移后吞吐量计算

第一部分:最忙组减少的负载

c 复制代码
tmp = SCHED_LOAD_SCALE * SCHED_LOAD_SCALE / busiest->cpu_power;
if (max_load > tmp)
    pwr_move += busiest->cpu_power * min(SCHED_LOAD_SCALE, max_load - tmp);

计算tmp

c 复制代码
tmp = 128 × 128 / busiest_cpu_power

含义:最忙组减少一个标准负载单位(128)对应的负载量

设计原理:考虑CPU功率差异,高性能CPU减少相同负载需要迁移更多任务

第二部分:当前组增加的负载

c 复制代码
tmp = SCHED_LOAD_SCALE * SCHED_LOAD_SCALE / this->cpu_power;
if (max_load < tmp)
    tmp = max_load;
pwr_move += this->cpu_power * min(SCHED_LOAD_SCALE, this_load + tmp);

计算逻辑

  • 计算当前组增加一个标准负载单位对应的负载量
  • 如果max_load较小,使用实际值
  • 累加到迁移后吞吐量

最终吞吐量计算

c 复制代码
pwr_move /= SCHED_LOAD_SCALE;

含义:将迁移后的吞吐量标准化。

8.5.经济性决策

c 复制代码
if (pwr_move < pwr_now + SCHED_LOAD_SCALE / 8)
    goto out_balanced;

条件分析

text 复制代码
迁移后吞吐量 < 当前吞吐量 + 16  (128/8)

设计原理 :只有迁移能带来至少1/8 CPU的吞吐量提升时才执行迁移

决策逻辑

  • 如果收益太小,放弃迁移
  • 否则,设置最小迁移量(*imbalance = 1

9.不平衡量转化为实际任务数

c 复制代码
*imbalance = (*imbalance + 1) / SCHED_LOAD_SCALE;
  • 实际任务数 = (负载量 + 1) / 缩放因子
  • +1确保至少迁移1个任务

10.特殊空闲状态处理

c 复制代码
out_balanced:
if (busiest && (idle == NEWLY_IDLE ||
                (idle == SCHED_IDLE && max_load > SCHED_LOAD_SCALE)) ) {
    *imbalance = 1;
    return busiest;
}

空闲CPU的特殊规则

  • NEWLY_IDLE:刚变空闲,积极迁移
  • SCHED_IDLE:调度器空闲,只有负载较重时才迁移

11.完整算法流程

text 复制代码
1. 遍历所有调度组,计算负载
2. 找到最忙的远程组和当前组
3. 检查系统是否已经平衡
4. 计算需要迁移的不平衡量
5. 对小不平衡量进行经济性分析
6. 考虑空闲状态的积极迁移
7. 返回最忙组和需要迁移的负载量

二、最忙运行队列查找函数find_busiest_queue

c 复制代码
static runqueue_t *find_busiest_queue(struct sched_group *group)
{
        unsigned long load, max_load = 0;
        runqueue_t *busiest = NULL;
        int i;

        for_each_cpu_mask(i, group->cpumask) {
                load = source_load(i);

                if (load > max_load) {
                        max_load = load;
                        busiest = cpu_rq(i);
                }
        }

        return busiest;
}

这个函数在给定的调度组内查找负载最高的单个CPU运行队列

1. 函数原型和目的

c 复制代码
static runqueue_t *find_busiest_queue(struct sched_group *group)

功能:在调度组的所有CPU中,找到负载最高的那个CPU的运行队列

参数

  • group:要搜索的调度组

2. 变量初始化

c 复制代码
unsigned long load, max_load = 0;
runqueue_t *busiest = NULL;
int i;

变量说明

  • load:当前CPU的负载值
  • max_load:遇到的最大负载值
  • busiest:指向最忙运行队列的指针
  • i:循环计数器,表示CPU编号

3. 循环遍历组内所有CPU

c 复制代码
for_each_cpu_mask(i, group->cpumask) {

for_each_cpu_mask

  • 遍历group->cpumask位图中设置的所有CPU
  • group->cpumask:位图,标识该调度组包含哪些CPU

4. 负载计算和比较

c 复制代码
load = source_load(i);

if (load > max_load) {
    max_load = load;
    busiest = cpu_rq(i);
}

source_load(i) 函数

  • 返回CPU i的负载 ,取 **平均负载 **和 瞬时负载的较小值
  • 对负载变化不敏感,防止出现乒乓效应

cpu_rq(i) 函数

  • 返回CPU i对应的运行队列指针
  • 每个CPU有自己独立的运行队列
相关推荐
IT成长日记3 小时前
【Nginx开荒攻略】静态文件服务深度解析:MIME类型映射与优化实战
linux·运维·服务器·nginx·mime
武大打工仔4 小时前
如何使用 Alacritty 让你的 macOS 终端更加出色
linux
深思慎考4 小时前
LinuxC++——etcd分布式键值存储系统入门
linux·c++·etcd
爱倒腾的老唐4 小时前
02、命令行的介绍
linux·bash
mahuifa5 小时前
C++(Qt)软件调试---Linux动态库链接异常排查(38)
linux·c++·动态库·ldd·异常排查
深思慎考5 小时前
LinuxC++——etcd分布式键值存储系统API(libetcd-cpp-api3)下载与二次封装
linux·c++·etcd
前方一片光明6 小时前
Linux—升级openssh常见的问题与解决方案
linux·运维·服务器
siriuuus7 小时前
Linux rsyslog 日志服务及日志转发实践
linux·rsyslog
dawnsky.liu7 小时前
RHEL - 在离线的 RHEL 10 中部署 Lightspeed 命令行助手
linux·人工智能·ai