linux cpu核间负载不均本质是cfs调度器未能及时重分布任务,需结合mpstat、pidstat、numastat定位真实瓶颈,再调优sched_migration_cost_ns等参数及中断分布。

Linux 中 CPU 核间负载不均,本质是调度器未能及时将任务在空闲 CPU 上重新分布,常见于高并发、绑核(taskset/cpuset)、NUMA 架构或突发流量场景。调优核心不是“强行平均”,而是让 CFS 调度器更合理地感知负载、迁移任务、尊重拓扑。
检查真实负载分布与瓶颈根源
先确认是否真有问题,避免误判:
- 用 mpstat -P ALL 1 观察各核 %idle、%iowait、%irq,注意区分是计算密集型不均,还是软中断/硬中断集中(如网卡收包只压在 CPU0)
- 用 pidstat -t -p PID 1 查看关键进程线程绑定情况(看 TGID 和 TID 的 CPU 列),确认是否被显式绑核(sched_setaffinity)
- 运行 numastat 和 lscpu,识别 NUMA 节点拓扑——跨节点迁移代价高,调度器会主动避免,此时“不均”可能是合理行为
调整 CFS 负载均衡关键参数
内核 4.12+ 默认启用 auto-Group Scheduling 和改进的 SD_ASYM_PACKING,但仍需按场景微调:
- /proc/sys/kernel/sched_migration_cost_ns:默认 500000(0.5ms),表示判断一个任务“轻量”的阈值。值过小会导致频繁迁移开销;过高则冷核长期闲置。高吞吐服务可适当提高(如 1000000)
- /proc/sys/kernel/sched_latency_ns:调度周期,默认 6ms。若系统有大量短时任务,可略微缩短(如 4000000),加快负载重分布节奏
- /proc/sys/kernel/sched_min_granularity_ns:最小调度粒度,默认 0.75ms。太小增加调度开销,太大导致小任务延迟;建议保持默认或略增(如 1000000)
- 对 NUMA 系统,确保 /proc/sys/kernel/sched_domain/cpu*/domain*/flags 中包含 SD_BALANCE_EXEC 和 SD_BALANCE_FORK,但关闭 SD_BALANCE_NEWIDLE(避免空闲时盲目迁移)
优化中断与软中断分布
CPU 负载不均常由中断扎堆引起,和进程调度无关:
- 查看 /proc/interrupts,确认网卡、NVMe、时钟中断是否集中在少数核。用 echo $MASK > /proc/irq/$IRQ/smp_affinity_list 手动分散(如双路 CPU 可轮询分配到不同节点)
- 启用 RPS(Receive Packet Steering)和 RFS(Receive Flow Steering):在 /sys/class/net/ethX/queues/rx-*/rps_cpus 写入对应掩码,让软中断处理靠近应用线程所在 CPU
- 对多队列网卡,确保开启 XPS(Transmit Packet Steering):echo $MASK > /sys/class/net/ethX/queues/tx-*/xps_cpus
谨慎使用绑核与 cpuset
手动绑核易破坏自动均衡,仅在明确收益场景使用:
- 数据库(如 postgresql)或低延迟服务,可将主线程 + WAL writer + bgwriter 绑定到同一 NUMA 节点内一组 CPU,同时隔离 IRQ 和其他干扰任务
- 用 cpuset 划分资源时,确保 cpuset.sched_load_balance=1(默认),否则子 cgroup 内部不均衡也无法跨组均衡
- 避免混合绑核策略:比如用 taskset 绑进程,又用 IRQ affinity 分散中断——可能造成 cache thrashing 和伪共享
调优后用 perf sched record -a sleep 30 && perf sched latency 对比迁移次数、最大延迟变化。不复杂但容易忽略的是:先确认问题类型,再选工具,最后调参。