简介

在当下服务器、高性能算力集群、多核多路 CPU 服务器以及大型嵌入式异构算力平台中,NUMA 非统一内存访问架构已经成为主流硬件架构。传统 SMP 对称多处理器架构下所有 CPU 访问内存时延一致的设计早已无法满足大内存、高并发、多核集群场景需求,而 NUMA 架构将整机硬件划分为多个独立节点,每个节点拥有专属 CPU 核组、本地物理内存与互联总线,不同节点之间依靠高速互联通道完成数据交互。

从硬件底层特性来讲,CPU 访问本地节点内存时延极低、带宽充足;访问跨远端节点内存不仅访问延迟成倍上涨,还会抢占节点间互联总线带宽,极易引发全局内存访问拥堵、进程运行抖动、整机业务吞吐量断崖式下跌。Linux 内核调度子系统为适配 NUMA 硬件特性,自研整套NUMA 拓扑感知调度体系,核心依靠节点距离权重建模、自动 NUMA 均衡调度numa_balancing、内存页就近迁移、任务亲和性动态调整四大核心机制,从进程调度层面主动完成CPU 核、进程线程、物理内存页三者就近绑定。

对于后端服务器研发、云计算虚拟化工程师、内核调优工程师、高性能大数据开发以及实时业务运维人员而言,吃透 Linux 内核 NUMA 拓扑感知原理、节点距离计算逻辑、任务放置策略与均衡调度触发规则,是完成服务器整机性能调优、虚拟化算力资源合理分片、数据库内核进程绑核优化、高性能计算任务算力分片部署的核心必备能力。同时该部分内核源码逻辑也是操作系统调度架构研究、计算机体系结构论文撰写、大型集群调度方案设计的核心理论依据,熟练掌握能够从硬件底层理解 Linux 进程调度资源分配逻辑,彻底解决线上业务因 NUMA 异构访问带来的隐性性能瓶颈。

一、核心概念与专业术语解析

1.1 NUMA 架构基础定义

NUMA(Non-Uniform Memory Access):非统一内存访问架构,多路 CPU 服务器硬件分层架构,整机被划分为若干个NUMA Node 节点

  • 本地节点 Node:CPU 核心隶属于当前节点,节点内置独立物理内存、控制器;
  • 远端节点 Node:非当前 CPU 所属节点,跨节点内存访问需要经过 QPI/UPI 互联总线转发;
  • 核心差异:本地内存访问时延 << 跨节点内存访问时延,距离越远性能损耗越大。

1.2 NUMA 核心基础术语

  1. NUMA Node 节点 硬件最小资源单元,包含一组 CPU 逻辑核、本地内存空间、节点互联接口,服务器一般默认按物理 CPU 划分节点。
  2. Node Distance 节点距离 内核量化定义两个 NUMA 节点之间的访问开销权重值,是调度器判断任务是否迁移、内存是否搬迁的唯一依据,数值越大代表访问开销越高。
    • 本地节点距离:默认固定为10
    • 同插槽不同节点:距离一般16~20
    • 跨插槽远端节点:距离可达30~40
  3. numa_balancing NUMA 自动均衡机制 Linux 内核内置后台均衡调度线程,周期性扫描系统内进程内存访问特征,自动将进程任务进程占用的物理内存页向低距离本地节点迁移,弱化跨节点访问带来的性能损耗。
  4. 任务放置策略 内核调度器在进程创建、进程唤醒、负载均衡触发时,依据 NUMA 拓扑距离表,优先将进程放置在内存访问距离最近的 CPU 核心上运行。
  5. 内存局部性原理 进程运行时绝大多数内存访问集中在固定内存区域,调度器利用该原理完成进程与内存的就近聚合,最大化利用本地高速内存带宽。
  6. NUMA 亲和性 分为进程 CPU 亲和性、内存绑定亲和性,手动或内核自动限制进程仅在指定 NUMA 节点范围内调度运行。

1.3 内核关键数据结构

1.3.1 节点距离存储结构体
// kernel/include/linux/numa.h
#define NUMA_DISTANCE_MIN        10    // 本地节点最小距离基准
#define NUMA_DISTANCE_MAX        255   // 节点距离最大值

// 全局NUMA节点距离二维数组
extern u8 numa_distance[MAX_NUMNODES][MAX_NUMNODES];

/* NUMA节点基础信息结构体 */
struct node_data {
    unsigned long node_start_pfn;    // 节点内存起始页帧号
    unsigned long node_spanned_pages;// 节点总内存页数
    unsigned int node_id;            // 节点编号
    struct zone *node_zones[MAX_NR_ZONES];
};

代码释义numa_distance是内核拓扑感知核心数据表,二维数组形式存储任意两个节点之间的访问开销权重,所有任务放置、内存迁移策略全部以此数组为判定标准。

1.3.2 调度域 NUMA 拓扑结构体
// kernel/sched/sched.h
struct sched_domain {
    /* 基础调度域成员省略 */
    int numa_id;                     // 调度域所属NUMA节点ID
    int node_distance;               // 当前调度域与目标节点距离
    unsigned int numa_weight;         // NUMA节点算力权重
    bool numa_scan_enabled;          // 是否开启NUMA内存扫描
};

1.4 核心工作逻辑梳理

  1. 系统开机阶段:内核遍历硬件 ACPI 拓扑信息,识别所有 NUMA 节点、CPU 核归属、内存分布,初始化 numa_distance 距离矩阵
  2. 进程运行阶段:内核统计进程页表访问轨迹,判定进程内存主要驻留的 NUMA 节点;
  3. 均衡调度阶段:numa_balancing后台线程依据节点距离表,计算最优放置节点;
  4. 资源迁移阶段:完成进程线程迁移至就近 CPU、物理内存页迁移至本地节点;
  5. 负载失衡兜底:全局负载均衡触发时,优先选择低距离节点完成任务分流,规避高时延跨节点调度。

二、环境准备

2.1 软硬件环境适配要求

环境分类 详细配置要求
硬件平台 多路 CPU NUMA 架构服务器(2 节点 / 4 节点最优),x86_64 架构
操作系统 CentOS 7/8、Ubuntu 20.04/22.04 服务端版本
内核版本 Linux 5.4、5.10、5.15、6.1 主流 LTS 稳定内核
编译依赖 gcc、make、libncurses-dev、bison、flex、numactl-devel
调试工具 numactl、numastat、perf、ftrace、gdb、schedstat、hwloc
运行权限 服务器 root 超级管理员权限

2.2 内核编译 NUMA 核心配置

进入内核源码目录执行编译配置:

make menuconfig

必须开启内核核心配置项

CONFIG_NUMA=y                  // 全局开启NUMA架构支持
CONFIG_NUMA_BALANCING=y        // 开启自动NUMA均衡调度
CONFIG_NUMA_BALANCING_DEFAULT_ENABLED=y // 默认启用均衡机制
CONFIG_MEMORY_HOTPLUG=y        // 支持NUMA节点内存热插拔
CONFIG_SCHED_NUMA=y            // 调度器NUMA拓扑感知调度
CONFIG_HWLOC_SUPPORT=y         // 硬件拓扑信息解析支持
CONFIG_DEBUG_NUMA=y            // NUMA调试日志输出

保存配置后执行内核编译安装:

make -j$(nproc)
sudo make modules_install && sudo make install
sudo update-grub && reboot

2.3 环境基础工具安装

# Ubuntu/Debian 系列
sudo apt install numactl numastat hwloc -y

# CentOS/RHEL 系列
sudo yum install numactl numastat hwloc-devel -y

2.4 基础环境核验命令

# 1. 查看整机NUMA节点拓扑结构
numactl --hardware

# 2. 查看节点之间距离权重表
cat /sys/devices/system/node/node*/distance

# 3. 查看内核NUMA均衡调度开关状态
cat /proc/sys/kernel/numa_balancing

# 4. 查看进程NUMA内存访问统计
numastat -p $$

三、实际应用场景(300 字精简实战场景)

NUMA 拓扑感知与任务放置优化在企业级生产环境中应用范围极广。在中小型互联网后端服务器集群中,MySQL、PostgreSQL 等关系型数据库服务对内存访问时延极度敏感,运维人员结合内核 NUMA 节点距离拓扑,将数据库主进程固定放置在内存距离最近的 CPU 节点,配合 numa_balancing 自动完成缓冲池内存页本地迁移,大幅降低 SQL 查询响应时延。在云计算 KVM、QEMU 虚拟化场景下,云主机宿主机依靠 NUMA 拓扑感知机制,将虚拟机 vCPU 与宿主机物理 NUMA 节点精准绑定,依据节点距离规避跨节点资源分配,提升虚拟机算力稳定性。在大数据 Hadoop、Spark 分布式算力集群中,算力调度组件借助内核 NUMA 任务放置策略,将计算任务优先调度至本地数据存储节点,实现计算与存储就近融合。同时在工业实时服务器、AI 模型推理服务器场景中,通过节点距离权重限制高时延跨节点任务迁移,保障高负载业务运行平稳,彻底解决多核大内存服务器普遍存在的 NUMA 异构访问性能隐患。

四、实战案例与源码深度解析

4.1 内核 NUMA 节点距离初始化源码

系统上电初始化阶段,内核读取硬件 ACPI 表单,完成节点距离矩阵填充,核心源码:

// kernel/drivers/base/numa.c
void __init numa_init_distance_table(void)
{
    int src_node, dst_node;
    u8 init_dist;

    // 遍历所有存在的NUMA节点
    for (src_node = 0; src_node < MAX_NUMNODES; src_node++)
    {
        if (!node_online(src_node))
            continue;
        
        // 本地节点距离固定赋值为基准值10
        numa_distance[src_node][src_node] = NUMA_DISTANCE_MIN;

        // 解析硬件总线信息,填充远端节点距离
        for (dst_node = 0; dst_node < MAX_NUMNODES; dst_node++)
        {
            if(src_node == dst_node || !node_online(dst_node))
                continue;
            // 读取硬件底层定义的节点访问时延权重
            init_dist = arch_get_numa_node_distance(src_node, dst_node);
            // 限制距离数值合法范围
            numa_distance[src_node][dst_node] = clamp(init_dist, 10, 255);
        }
    }
    pr_info("NUMA node distance table init success\n");
}

代码作用:该函数是整机 NUMA 拓扑建模入口,完成全局距离矩阵初始化,后续所有调度迁移逻辑均读取此矩阵数值,架构层统一标准化节点访问开销。

4.2 调度器依据节点距离筛选最优任务放置节点

内核调度负载均衡时,优先筛选距离权重最小的节点作为任务目标放置节点,核心调度逻辑代码:

// kernel/sched/fair.c  NUMA任务择优放置逻辑
static int sched_numa_find_near_node(int cur_node, struct task_struct *p)
{
    int target_node;
    int min_distance = 255;
    int best_node = cur_node;

    // 遍历所有在线NUMA节点
    for(target_node = 0; target_node < nr_online_nodes; target_node++)
    {
        if(!node_online(target_node))
            continue;
        // 读取两个节点之间的距离权重
        int dist = numa_distance[cur_node][target_node];
        // 筛选距离最小的最优节点
        if(dist < min_distance)
        {
            min_distance = dist;
            best_node = target_node;
        }
    }
    // 返回最优就近放置节点ID
    return best_node;
}

代码注释:进程出现负载倾斜、需要跨节点分流时,调度器调用此函数,抛弃随机分配策略,严格按照节点距离大小择优分配,从源头减少跨节点内存访问。

4.3 numa_balancing 自动均衡调度核心触发逻辑

// kernel/sched/numa_balancing.c
static void numa_balancing_scan_processes(struct rq_flags *rf)
{
    struct task_struct *p;
    int mem_node;
    int cpu_node;

    // 遍历运行队列中所有就绪进程
    for_each_task_in_rq(p, rf)
    {
        // 获取进程主要内存驻留NUMA节点
        mem_node = task_mempolicy_node(p);
        // 获取进程当前运行CPU所属节点
        cpu_node = task_cpu(p) % nr_online_nodes;

        // 判定:进程CPU运行节点与内存节点距离过大,触发均衡迁移
        if(numa_distance[cpu_node][mem_node] > 20)
        {
            // 发起进程线程就近迁移
            task_numa_migrate(p, mem_node);
            // 发起进程物理内存页就近迁移
            migrate_task_memory_pages(p, mem_node);
        }
    }
}

实战逻辑:内核默认设置距离阈值,当进程运行 CPU 节点与内存驻留节点距离超过阈值,自动触发任务线程迁移 + 内存页迁移双向优化,实现软硬件资源聚合。

4.4 用户态实操:手动绑定进程至指定 NUMA 节点

编写 C 语言测试代码,手动指定进程绑定 NUMA 节点,模拟业务进程节点放置优化:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <numa.h>
#include <numaif.h>

int main(int argc,char *argv[])
{
    // 定义需要绑定的NUMA节点ID
    int bind_node = 0;
    // 初始化NUMA绑定掩码
    nodemask_t node_mask;
    nodemask_zero(&node_mask);
    nodemask_set(&node_mask, bind_node);

    // 将当前进程绑定至指定NUMA节点运行
    if(numa_available() >= 0)
    {
        numa_set_preferred(bind_node);
        // 强制进程内存仅从指定节点分配
        set_mempolicy(MPOL_BIND, nodemask_bits(&node_mask), MAX_NUMNODES);
        printf("进程已成功绑定至NUMA节点%d运行\n",bind_node);
    }
    else
    {
        printf("当前系统不支持NUMA架构\n");
    }

    // 模拟长时间业务运行负载
    while(1)
    {
        sleep(1);
    }
    return 0;
}

编译与运行命令:

gcc numa_bind_test.c -o numa_bind_test -lnuma
sudo ./numa_bind_test

使用场景:生产环境核心数据库、缓存服务均可通过该方式手动绑定 NUMA 节点,规避内核自动均衡带来的进程抖动。

4.5 运维命令实战:动态调整 NUMA 均衡调度参数

# 临时关闭全局NUMA自动均衡调度
echo 0 > /proc/sys/kernel/numa_balancing

# 临时开启全局NUMA自动均衡调度
echo 1 > /proc/sys/kernel/numa_balancing

# 查看系统所有进程NUMA内存分布状态
numastat

# 查看指定PID进程NUMA详细访问统计
numastat -p 进程PID

# 手动将进程迁移至指定NUMA节点
numactl --cpunodebind=1 --membind=1 ./业务程序

4.6 Ftrace 跟踪内核 NUMA 调度函数

# 挂载调试文件系统
mount -t debugfs none /sys/kernel/debug
echo > /sys/kernel/debug/tracing/trace

# 跟踪NUMA距离初始化、均衡迁移核心函数
echo numa_init_distance_table >> /sys/kernel/debug/tracing/set_ftrace_filter
echo task_numa_migrate >> /sys/kernel/debug/tracing/set_ftrace_filter
echo migrate_task_memory_pages >> /sys/kernel/debug/tracing/set_ftrace_filter

# 开启函数跟踪
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on

# 查看内核NUMA调度运行日志
cat /sys/kernel/debug/tracing/trace

五、常见问题与疑难解答

Q1:开启 numa_balancing 之后,业务进程出现频繁抖动卡顿是什么原因?

解答:自动均衡调度会周期性迁移进程内存页,大内存进程内存迁移过程会占用大量系统 IO 与 CPU 资源,引发业务抖动。解决方案:核心线上业务手动使用numactl固定节点绑定,关闭全局自动均衡调度,仅测试环境开启调优。

Q2:读取节点 distance 文件数值全部一致,内核无法识别真实 NUMA 拓扑?

解答:一是服务器 BIOS 未开启 NUMA 架构支持,进入 BIOS 开启NUMA Enabled;二是内核未开启CONFIG_NUMA编译选项;三是虚拟机环境默认屏蔽 NUMA 拓扑,物理服务器方可正常识别距离权重。

Q3:进程已经绑定 NUMA 节点,依旧出现大量跨节点内存访问?

解答:进程子线程、动态库独立内存分配未继承主进程绑定策略,同时系统内核缓冲区、页缓存会随机分配远端节点内存。解决方式:统一绑定所有工作线程,限制内存分配策略为严格节点绑定。

Q4:多核高负载场景下,NUMA 节点距离调度策略失效,负载严重倾斜?

解答:内核默认 NUMA 均衡扫描频率较低,高并发场景来不及完成资源迁移。可通过修改内核源码调高numa_balancing扫描频次,或者手动划分 CPU 资源池,按 NUMA 节点拆分业务集群。

Q5:不同内核版本之间 NUMA 任务放置逻辑是否存在差异?

解答:5.4 及以下老版本内核 NUMA 均衡算法粗糙,距离权重利用率低;5.10 及以上新版本重构了 NUMA 调度域逻辑,节点距离加权计算更加精准,生产环境优先选用 5.10 以上 LTS 内核。

六、实践调优建议与行业最佳实践

  1. 物理服务器 BIOS 层级优化 生产环境多路 CPU 服务器务必在 BIOS 中开启 NUMA 功能,关闭 SMP 兼容模式,让操作系统精准识别硬件节点拓扑与原生节点访问时延,为内核距离建模提供精准硬件数据。

  2. 线上业务分级调度策略 核心时延敏感业务(数据库、消息队列)采用手动 NUMA 节点硬绑定,彻底禁用自动均衡;后台离线计算、日志分析、数据备份等非敏感业务,开启numa_balancing自动均衡,充分利用空闲算力资源。

  3. CPU 分片与 NUMA 节点对齐原则 进行服务器 CPU 资源分片、容器 CPU 配额划分时,严格以完整 NUMA 节点 CPU 核组为最小划分单位,禁止拆分同一个 NUMA 节点内 CPU 核心打散分配,破坏本地内存访问局部性。

  4. 内核调优参数固化方案numa_balancing内核参数写入sysctl.conf永久生效,线上稳定业务统一设置为关闭自动均衡,测试调优服务器开启,区分环境统一规范。

  5. 内核源码二次开发优化思路 自研定制调度系统时,可基于原生numa_distance距离矩阵,新增业务自定义权重阈值,针对 AI 推理、实时音视频等特殊业务,定制专属就近任务放置优先级,适配垂直场景调度需求。

  6. 故障排查标准流程 出现内存访问性能瓶颈时,优先使用numastat确认跨节点内存访问占比,再查看 NUMA 节点距离拓扑表,最后核查进程绑定状态,逐层定位是硬件拓扑问题还是内核调度策略问题。

七、全文总结与工程落地延伸

本篇文章系统性从 NUMA 硬件架构底层原理、内核核心数据结构、节点距离拓扑建模流程、numa_balancing 均衡调度机制、任务就近放置核心源码、用户态实操开发、线上调优排障全维度,完整拆解了 Linux 内核 NUMA 拓扑感知整套调度体系。

内核通过硬件信息采集完成节点距离量化建模,将抽象的内存访问时延转化为可计算、可判定的权重数值,再依托调度域拓扑感知能力,在进程创建、负载均衡、内存访问异常等时机,完成任务线程与物理内存的双向就近优化,从操作系统调度层面抹平 NUMA 架构带来的异构访问性能差距,这也是大型多路服务器能够稳定承载超高并发业务的核心底层支撑。

在实际工程落地中,NUMA 拓扑感知调度不仅应用在传统服务器业务调优,更是云计算虚拟化资源调度、算力调度平台、边缘异构嵌入式系统、高性能科学计算集群的核心底层调度基础。建议各位从业者结合文中内核源码、测试程序、运维调优命令,在物理 NUMA 服务器中复现实验,观察不同节点距离下进程运行时延、内存访问带宽变化,真正做到吃透原理、落地调优。同时可基于本文理论内容,拓展研究 NUMA 混合内存架构调度、异构 NUMA 算力节点负载均衡等进阶方向,进一步深耕 Linux 高级调度子系统技术体系。

Logo

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

更多推荐