简介

在多核乃至多路 NUMA 架构服务器、工业嵌入式多核平台、云计算宿主机集群中,CPU 硬件架构早已从单核演进至超线程、多核心、多 CPU 插槽分布式拓扑结构。传统单核心调度逻辑无法适配多硬件层级算力分配,极易出现CPU 核心空载扎堆、部分核心负载满载卡死、跨节点访问延迟飙升等典型性能问题。

Linux 内核为解决多核算力分配不均问题,设计出一套完整的调度域分层负载均衡体系,依托 SMT、MC、NUMA 三级调度域完成精细化任务分流与迁移。该均衡机制区别于简单的进程均分策略,会根据硬件拓扑层级制定差异化迁移规则:超线程层级优先激进均衡快速拉平负载,NUMA 跨节点层级采取保守均衡策略规避远程内存访问开销。

当前无论是服务器性能调优、嵌入式实时系统多核调度优化、云服务器算力资源调度,还是操作系统内核裁剪、实时操作系统改造,都必须吃透分层调度域与任务迁移逻辑。对于底层驱动工程师、Linux 运维性能专家、嵌入式开发人员以及撰写内核相关论文的研究者而言,掌握负载均衡分层架构、各级域均衡触发时机、任务迁移决策条件,是定位 CPU 负载倾斜、优化多核并发吞吐量、降低跨核调度延迟的核心必备能力。本文从底层硬件拓扑映射、核心源码逻辑、实操调试、工程落地全维度讲解,内容贴合线上生产环境,可直接用于技术报告撰写、内核源码研读与线上性能问题排查。

一、核心概念与基础术语解析

1.1 多核硬件拓扑层级划分

Linux 内核会自动识别物理 CPU 硬件架构,自上而下划分三层标准硬件拓扑,也是调度域分层设计的物理依据:

  1. SMT 层(同步多线程层) 即 CPU 超线程层级,同一个物理核心下拆分出的多个逻辑 CPU,共享 L1、L2 缓存,硬件资源高度互通,核间通信延迟极低。
  2. MC 层(多核核心层) 同一物理 CPU 插槽内的多个物理核心,共享 L3 缓存,内存访问路径一致,属于本地近距离核心集群。
  3. NUMA 层(非统一内存访问层) 多 CPU 插槽组成的分布式架构,不同插槽核心隶属于不同 NUMA 节点,节点之间内存物理地址独立,跨节点访问内存带宽低、延迟高、资源开销极大。

1.2 调度域与调度组核心定义

  • 调度域 struct sched_domain 内核中用于描述一组具备相同调度均衡规则的 CPU 集合,每一层硬件拓扑对应独立层级调度域,域内定义均衡周期、迁移阈值、迁移倾向、空闲判定等核心均衡参数,是负载均衡的管理单元。
  • 调度组 struct sched_group 隶属于调度域,是实际承载 CPU 负载统计、负载计算的最小单元,一个调度组包含一个或多个逻辑 CPU,均衡算法以调度组为单位计算平均负载。
  • 负载均衡时机 分为周期性主动均衡空闲触发被动均衡,周期性均衡由内核时钟定时触发,空闲均衡在 CPU 进入空闲状态时主动拉取空闲任务。

1.3 负载均衡核心分类

  1. 普通 CFS 负载均衡:针对分时调度 SCHED_OTHER 普通业务进程,追求整体 CPU 利用率均衡,兼顾交互响应速度。
  2. 实时负载均衡:针对 RT 实时任务与 Deadline 硬实时任务,均衡优先级更低,优先保障实时任务运行时序,避免迁移抢占引发时序错乱。
  3. 分层均衡策略差异
    • SMT 层级:激进均衡,均衡周期短、负载差值阈值小,只要出现轻微负载差立刻执行任务迁移,最大化利用超线程算力。
    • MC 层级:中度均衡,平衡负载与缓存命中率,适度迁移避免同插槽核心负载两极分化。
    • NUMA 层级:保守均衡,均衡周期极长、负载阈值极大,非极端负载倾斜绝不触发跨 NUMA 节点任务迁移,优先规避远程内存访问损耗。

1.4 任务迁移核心判定指标

内核进行任务迁移决策时,核心参考三大指标:

  • 调度组平均负载值、空闲 CPU 数量
  • 任务进程缓存亲和性、进程内存分布节点
  • 硬件层级迁移开销、均衡触发冷却时间

二、环境准备

2.1 软硬件环境配置

环境类别 详细配置要求
操作系统 Ubuntu 20.04/22.04 Server、CentOS 7/9 主流服务端系统
内核版本 Linux 5.4、5.10、5.15、6.1 主流 LTS 长期稳定内核
硬件架构 支持 SMT 超线程、多核心、NUMA 架构 x86_64 服务器
编译依赖 gcc、make、libncurses-dev、bison、flex、numactl 工具集
调试工具 perf、trace-cmd、ftrace、schedstat、mpstat、pidstat
查看工具 lscpu、numastat、taskset、ps、top

2.2 环境依赖安装与基础配置

1. 安装多核架构查看与性能调试工具
# Ubuntu/Debian 系列安装命令
sudo apt update
sudo apt install numactl numactl-tools perf linux-tools-common trace-cmd

# CentOS/RHEL 系列安装命令
sudo yum install numactl perf trace-cmd
2. 查看本机 CPU 硬件拓扑与调度域信息
# 查看完整CPU层级架构、SMT、NUMA节点分布
lscpu

# 查看系统NUMA节点内存、CPU绑定状态
numastat -m

# 查看当前系统所有逻辑CPU负载统计
mpstat -P ALL 1
3. 内核编译开启调度调试选项

重新编译内核需开启调度域调试、负载均衡日志相关配置,执行make menuconfig开启以下选项:

CONFIG_SCHED_DEBUG=y          # 调度器全局调试开关
CONFIG_SCHED_DOMAIN_DEBUG=y   # 调度域分层调试日志
CONFIG_FTRACE_SCHED=y         # 调度流程函数跟踪
CONFIG_NUMA_BALANCING=y       # 开启NUMA自动负载均衡
CONFIG_SMP=y                  # 多核SMP调度基础支持

配置完成后正常编译安装内核并重启生效。

2.3 内核核心源码路径

负载均衡与调度域核心源码存放路径,方便读者溯源研读:

kernel/sched/sched.h        // 调度域、调度组结构体定义
kernel/sched/fair.c         // CFS调度器分层负载均衡核心逻辑
kernel/sched/topology.c     // 硬件拓扑解析、调度域分层构建逻辑
kernel/sched/migration.c    // 任务迁移核心函数、迁移队列管理

三、实际应用场景

分层调度域负载均衡架构是所有多核业务场景的底层调度基石。在云计算 KVM 虚拟化场景中,宿主机依靠 SMT 层级激进均衡快速打散轻量云主机进程,通过 MC 层级均衡平衡同一 CPU 插槽内虚拟机算力占用,严格限制 NUMA 跨节点迁移,避免虚拟机内存访问延迟过高导致业务卡顿。

在工业控制多核嵌入式设备中,实时控制任务绑定本地 NUMA 节点核心运行,普通日志、采集业务通过中层调度域均衡分流,既保障硬实时任务时序稳定,又充分利用剩余空闲算力。在大数据分布式计算、流媒体转码集群等高并发业务场景下,内核依靠分层均衡策略,将批量计算任务优先均衡至本地同插槽核心,仅在整机负载严重失衡时才触发跨 NUMA 节点迁移,在算力均衡与硬件访问开销之间做到最优平衡,有效提升整机并发处理能力,降低多核调度带来的性能损耗。

四、核心原理与实战代码案例

4.1 调度域分层构建底层原理

系统开机初始化阶段,内核会调用build_sched_domains函数遍历 CPU 硬件拓扑,从底层 SMT 向上逐层创建调度域,为每一层调度域初始化专属均衡参数。

4.1.1 调度域核心结构体精简源码
// kernel/sched/sched.h 调度域核心结构体
struct sched_domain {
    /* 指向当前层级上一层、下一层调度域,实现层级串联 */
    struct sched_domain *parent;
    struct sched_domain *child;

    /* 当前调度域包含的CPU掩码,标记域内所有可用逻辑CPU */
    cpumask_var_t span;

    /* 均衡触发周期,单位为内核节拍,层级越高周期越长 */
    unsigned int balance_interval;

    /* 负载均衡阈值,负载差值超过阈值才触发迁移 */
    unsigned int imbalance_pct;

    /* 调度域内调度组链表头 */
    struct sched_group *groups;

    /* 空闲CPU判定阈值、任务迁移优先级标识 */
    unsigned int busy_factor;
    unsigned int idle_balance;
};

代码注释说明balance_intervalimbalance_pct是分层策略核心,SMT 域该数值最小,NUMA 域数值最大,直接决定各级域均衡激进程度。

4.1.2 硬件拓扑生成调度域逻辑源码
// kernel/sched/topology.c 简化版调度域分层构建函数
static void init_sched_domains_by_topo(void)
{
    // 1. 优先解析SMT超线程层级,构建最内层调度域
    build_smt_sched_domain();
    // 2. 基于SMT域向上聚合,构建同一插槽多核MC调度域
    build_mc_sched_domain();
    // 3. 聚合所有CPU插槽,构建顶层NUMA跨节点调度域
    build_numa_sched_domain();

    // 为不同层级调度域初始化差异化均衡参数
    set_smt_balance_param();
    set_mc_balance_param();
    set_numa_balance_param();
}

运行逻辑:内核自下而上完成三层调度域搭建,层级越低均衡规则越宽松,层级越高均衡限制越严格。

4.2 分层负载均衡差异化策略实现

4.2.1 各级调度域核心参数差异对照表
  1. SMT 调度域 均衡周期短、负载失衡阈值低,CPU 空闲时优先立刻迁移同核心超线程任务,最大化利用共享缓存资源,适合轻量高频次任务均衡。
  2. MC 调度域 均衡周期适中,兼顾负载均衡与 CPU 缓存命中率,避免频繁迁移导致进程缓存失效,适配普通业务进程调度。
  3. NUMA 调度域 默认关闭主动频繁均衡,仅整机负载差异极大时触发,优先保留任务本地内存访问特性,严控跨节点迁移数量。
4.2.2 负载均衡触发核心函数
// kernel/sched/fair.c CFS负载均衡主入口函数
static void trigger_load_balance(struct rq *rq, int cpu)
{
    struct sched_domain *sd;
    // 从当前CPU所属最底层调度域向上逐层遍历
    for (sd = rq->sd; sd; sd = sd->parent)
    {
        // 判断当前层级是否满足均衡触发条件
        if (!need_balance(sd, rq))
            continue;
        // 执行当前层级调度域任务负载均衡
        do_sched_balance(cpu, sd);
        // 低层均衡完成后,不再向上层NUMA域发起均衡
        if (sd->child)
            break;
    }
}

代码作用:负载均衡遵循从下至上优先均衡原则,低层 SMT、MC 域完成负载拉平后,直接终止遍历,不会轻易触发高层 NUMA 域均衡,完美契合保守均衡设计思想。

4.3 任务迁移决策与筛选逻辑源码

4.3.1 可迁移任务筛选函数

内核不会随意迁移所有进程,会优先筛选无 CPU 亲和绑定、缓存开销小、优先级低的普通进程进行迁移:

static int can_migrate_task(struct task_struct *p, int src_cpu, int dst_cpu)
{
    // 1. 实时任务禁止随意迁移,保障实时性
    if (rt_task(p))
        return 0;
    // 2. 进程手动绑定CPU,跳过迁移
    if (!cpumask_test_cpu(dst_cpu, &p->cpus_allowed))
        return 0;
    // 3. 高频交互进程减少迁移,避免交互卡顿
    if (task_have_high_interactive(p))
        return 0;
    // 满足所有条件允许迁移
    return 1;
}
4.3.2 跨层级任务迁移执行逻辑
static int task_migrate_to_group(struct sched_group *src_grp,struct sched_group *dst_grp)
{
    struct task_struct *task;
    // 遍历过载调度组内就绪队列任务
    list_for_each_entry(task, &src_grp->rq->cfs_tasks, se.group_node)
    {
        // 校验任务是否符合迁移条件
        if(!can_migrate_task(task, src_grp->cpu, dst_grp->cpu))
            continue;
        // 执行进程CPU迁移,修改进程运行CPU标识
        set_task_cpu(task, dst_grp->cpu);
        // 完成任务调度队列切换
        move_task_to_rq(task, src_grp->rq, dst_grp->rq);
        // 单次均衡仅迁移固定数量任务,防止批量迁移抖动
        return 1;
    }
    return 0;
}

4.4 实操测试:手动模拟 CPU 负载倾斜验证均衡

4.4.1 编写压测程序绑定指定 CPU 制造负载
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>

// 死循环CPU密集型压测程序
void cpu_stress(void)
{
    while(1)
    {
        // 纯浮点运算占用CPU资源
        double a = 0;
        for(int i=0;i<100000;i++)
            a += i * 3.1415;
    }
}

int main(int argc,char *argv[])
{
    cpu_set_t mask;
    int cpu_id = 0;
    // 绑定进程至0号CPU核心
    CPU_ZERO(&mask);
    CPU_SET(cpu_id,&mask);
    sched_setaffinity(0,sizeof(mask),&mask);

    printf("进程已绑定CPU%d 开始压测\n",cpu_id);
    cpu_stress();
    return 0;
}

编译运行命令:

gcc cpu_stress.c -o cpu_stress
# 后台运行制造单核高负载
./cpu_stress &
4.4.2 监控负载均衡自动分流过程
# 实时查看所有CPU负载变化
mpstat -P ALL 2

# 利用ftrace跟踪负载均衡调用函数
echo do_sched_balance >> /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on

# 查看均衡跟踪日志
cat /sys/kernel/debug/tracing/trace

实操现象:0 号 CPU 满载后,SMT 同层级逻辑 CPU 会快速触发均衡分流,同插槽 MC 核心缓慢分担负载,跨 NUMA 节点 CPU 几乎不会介入任务迁移。

4.4.3 手动修改进程 CPU 亲和性测试迁移
# 查询进程PID
pidof cpu_stress
# 将进程强制迁移至4号CPU核心
taskset -p 0x10 进程PID

五、常见问题与解答

Q1:为什么服务器 CPU 整体利用率不高,却出现局部核心满载卡死?

解答:大概率是进程设置了 CPU 亲和性,限制进程只能运行在指定核心,内核分层负载均衡无法筛选并迁移该进程;其次部分实时业务进程优先级过高,普通均衡策略无法抢占迁移,最终造成负载倾斜。

Q2:开启 NUMA 自动均衡后业务延迟反而升高如何解决?

解答:NUMA 均衡会触发跨节点内存数据同步迁移,产生巨大 IO 开销。线上业务建议关闭自动 NUMA 均衡,手动将业务进程绑定至本地 NUMA 节点 CPU,依靠低层 SMT、MC 调度域完成负载均衡即可。

Q3:超线程服务器关闭 SMT 后,负载均衡策略会发生哪些变化?

解答:关闭超线程后内核不再构建 SMT 层级调度域,直接启用 MC 层级作为最底层均衡单元,均衡触发频率降低,任务迁移数量减少,CPU 缓存命中率提升,但整机并行算力会下降。

Q4:如何确认系统负载均衡是卡在 SMT 层还是 NUMA 层?

解答:通过 ftrace 跟踪do_sched_balance调用栈,查看调用来源调度域层级;也可通过schedstat查看各级域均衡执行次数,次数最多的层级为当前主力均衡层级。

Q5:高并发网络服务频繁出现调度抖动,是否和分层均衡有关?

解答:有关,短时间大量新进程创建会触发高频多层级负载均衡,批量任务迁移造成调度抖动。解决方案是调大均衡冷却时间,减少高峰期自动迁移次数。

六、实践建议与最佳实践

  1. 线上服务器调度域优化 生产环境业务服务器优先关闭 NUMA 自动均衡,业务进程按业务模块绑定固定 NUMA 节点,仅依靠 SMT 与 MC 中层调度域完成负载均衡,兼顾算力均衡与访问延迟。

  2. 嵌入式多核设备调度配置 工业嵌入式设备划分核心分区,实时控制核心关闭负载均衡,普通业务核心开启低层激进均衡,彻底隔离实时任务与普通任务调度互相干扰。

  3. 性能调优调试技巧 排查负载不均问题时,优先使用mpstat定位满载核心,再通过 ftrace 抓取负载均衡函数调用日志,确认均衡是否正常触发,区分是均衡策略问题还是进程亲和性限制问题。

  4. 内核参数优化建议 通过修改/proc/sys/kernel/下调度相关内核参数,微调各级调度域均衡阈值,业务高峰期适当拉长均衡周期,闲时缩短周期完成负载规整。

  5. 进程部署规范 CPU 密集型计算业务尽量集中部署在同一 CPU 插槽内,避免分散部署触发高层 NUMA 迁移;IO 密集型轻量业务可放开限制,依靠底层 SMT 均衡灵活调度。

七、全文总结与技术延伸

本文系统性梳理了 Linux 内核分层调度域架构设计思想,从 SMT、MC、NUMA 三层硬件拓扑对应三层调度域,深度拆解了不同层级差异化负载均衡策略,详解了负载均衡触发时机、任务筛选规则、进程迁移完整执行流程,搭配内核源码、用户态压测代码、线上调试命令完成全流程实战讲解。

分层调度域负载均衡是 Linux 多核调度体系的核心骨架,其低层激进、高层保守的设计思想,完美平衡了多核算力均分与硬件访问性能损耗两大核心矛盾。在云计算虚拟化、工业实时控制系统、大数据集群、边缘多核嵌入式设备等主流场景中,这套迁移策略都是保障系统稳定运行、挖掘多核硬件性能的关键。

对于内核研发人员,可以基于现有调度域框架自定义均衡策略,定制化开发专属业务调度规则;对于运维与性能优化工程师,熟练掌握分层均衡原理能够快速解决线上 CPU 负载倾斜、调度延迟过高、业务卡顿等疑难问题。建议读者结合自身服务器硬件架构,复现本文测试案例,结合实际业务场景调整负载均衡参数,真正将内核调度底层原理落地到工程项目实战当中。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐