Linux 不搞”一个全局 runqueue”——每 CPU 独立 runqueue,锁本地,不争不抢。代价是需要主动搬任务才不会出现”3 个 CPU 空转,1 个 CPU 排队”。负载均衡机制从 2003 年 SMP 以来演化至今,层次繁多。本文系统讲解。
一、先看图:调度域层级
flowchart TB
NODE[NUMA Node · DIE]
NODE --> MC0[MC (socket) 0]
NODE --> MC1[MC (socket) 1]
MC0 --> CORE00[Core 0]
MC0 --> CORE01[Core 1]
CORE00 --> SMT0[HT 0]
CORE00 --> SMT1[HT 1]
CORE01 --> SMT2[HT 2]
CORE01 --> SMT3[HT 3]
classDef l0 fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef l1 fill:#3fb95022,stroke:#3fb950,color:#adbac7;
classDef l2 fill:#f0883e22,stroke:#f0883e,color:#adbac7;
classDef l3 fill:#a371f722,stroke:#a371f7,color:#adbac7;
class NODE l0
class MC0,MC1 l1
class CORE00,CORE01 l2
class SMT0,SMT1,SMT2,SMT3 l3
每层对应一个 sched_domain:SMT、MC
(Multi-Core, cluster/socket)、DIE(整颗芯片)、NUMA (跨
socket)。每层有自己的 migration cost(小 →
大)和 均衡频率。
二、均衡的四个触发时机
2.1 wake-up 时
新任务或唤醒任务要放哪个 CPU?
- wake-affine:优先放在 waker 所在 CPU(利用 cache / 局部性)
- 若 waker CPU 太忙 → 找同 LLC (last-level cache) 内最空
- 若都忙 → 跨 LLC 甚至跨 NUMA
关键启发式
WAKE_AFFINE、SD_BALANCE_WAKE flag
在 sched domain 里。
2.2 新任务 fork/exec 时
select_task_rq_fair 决定落在哪。倾向”父 CPU
的同 LLC 组里的空核”——新进程往往和父共享 data。
2.3 周期性 balance
每 CPU tick 检查
rq->next_balance,到期扫描 sched_domain
看是否要 migration。频率随域变化(SMT 1ms、MC 4ms、NUMA
100ms+ 不等,都是经验值)。
2.4 idle balance (pull)
CPU 空转前的最后一刻:去其他 CPU “拉”
任务过来。newidle_balance
函数。这是最激进的均衡,空闲 CPU
宁可迁移一个任务也不闲置。
三、imbalance 判定
不是”有差就迁”——迁移有代价(cache 冷、TLB 失效)。判定大致:
imbalance = busiest.load_avg - local.load_avg
if (imbalance > threshold) migrate
threshold 随域增大(高层迁移更贵)。6.x 后
SD_ASYM_CPUCAPACITY 加入 capacity
差异(大小核),不只看 load。
四、NUMA Balancing
多 NUMA node 场景:任务 A 的内存在 node 0,任务跑到 node 1 → 远程访问慢 3-5x。
方案 1:把任务挪回(task migration) 方案 2:把页迁过来(page migration) 方案 3:都不动,接受性能损失
Linux 的 NUMA Balancing 周期性:
- 扫描任务 VMA,把一些页标记 “PROT_NONE”
- 触碰即 fault,记录”是哪个 node 访问的”
- 统计积累后决定:迁页 或 迁任务
/proc/sys/kernel/numa_balancing
开关;/sys/kernel/mm/mempolicy/... 调。
坏处:fault 本身有成本。HPC 场景往往关 NUMA balancing 用手动 numactl。
五、prefer_sibling 与 SMT
超线程(SMT)两线程共享 core 资源。优先填满不同 core 的
HT 才填同 core:SD_PREFER_SIBLING。
场景:2 CPU-bound 任务应分到 2 个物理核而不是挤在一个核的两 HT。
现代 Intel + AMD + ARM 都有 SMT(ARM big 核部分型号)。调度器自动识别 topology。
六、Energy-Aware Scheduling (EAS)
移动 / 客户端用的扩展:给每 CPU 一个 “能量模型”(频率 → 功耗曲线)。选 CPU 时不只看 load,还估计”哪放更省电”。
要开 CONFIG_ENERGY_MODEL + 厂商提供
energy-costs.dtsi。Android / Chromebook
默认开。
详见 C-25(big.LITTLE)。
七、诊断负载均衡
7.1 看 topology
lscpu --extended
# CPU NODE SOCKET CORE L1d L1i L2 L3
7.2 看域
cat /proc/schedstat # 老接口,较粗
ls /sys/kernel/debug/sched/domains/ # 每 CPU 每域统计
cat /sys/kernel/debug/sched/domains/cpu0/domain0/name
# → "MC"
7.3 事件
bpftrace -e 'tracepoint:sched:sched_migrate_task {
@migrations[cpu] = count();
}'
短时间看谁被迁去哪。
7.4 NUMA
numastat -p <pid>
cat /proc/<pid>/numa_maps
perf c2c 能抓跨 node cache line 争用。
八、常见问题
问题 A:两个 CPU 忙,另两个长期空
原因:cache-hot 标志让任务不愿迁走;或
imbalance 阈值高 解决:taskset
强绑;或调 kernel.sched_migration_cost_ns
问题 B:容器里 latency 飙
原因:cgroup 限额 + CPU 迁移 → cache 失效后
throttle;或调度域太大频繁迁
解决:cpu.cfs_period_us /
quota 调小;或 isolate 专用 CPU
问题 C:NUMA Balancing 反而让性能下降
原因:应用内存分布固定但被来回迁
解决:numactl --membind --cpunodebind;关
autonuma
问题 D:HT 独占性不够
原因:prefer_sibling 没生效或 topology
报告错
诊断:cat /sys/devices/system/cpu/cpu0/topology/thread_siblings_list
问题 E:多租户云上 noisy neighbor 原因:VM 间共享 LLC / HT 解决:Intel RDT (CAT) + cpuset + L3 隔离
九、工作负载与策略对照
| 负载 | 推荐策略 |
|---|---|
| 数据库(PostgreSQL、MySQL) | 绑 CPU(cpuset) + numactl 本 node + 关 NUMA bal |
| 高并发 HTTP | 默认即可,watch wake_affine |
| HPC MPI | numactl + rank pinning |
| AI 训练 | GPU 绑卡 + NUMA bal 关 |
| 实时 | isolcpus + nohz_full |
| 桌面 | 默认(EAS + autogroup) |
十、小结
- 每 CPU 一个 runqueue + 多层 sched_domain
- 四类触发:wake、fork、tick、idle
- imbalance 阈值与迁移代价挂钩
- NUMA Balancing 是双刃剑
- prefer_sibling 解 HT 问题;EAS 加能量维度
- 生产调优看应用类型选策略
下一篇 C-25 讲 big.LITTLE / Intel P+E 的异构多核调度——capacity-aware、misfit migration、Intel Thread Director。
参考文献
- Bovet & Cesati, Understanding the Linux Kernel, Ch. 7
Documentation/scheduler/sched-domains.rst- Corbet, J. “CFS group scheduling.” LWN.net 2008 / “The NUMA balancer” 2014
- Gorman, M. “Automatic NUMA Balancing.” RHEL engineering blog
- Dhiman, J. et al. “Energy-Aware Scheduling for Linux.” ELC 2018
- Intel RDT / ARM MPAM 手册
工具
lscpu -e、numactl -Hperf c2c record/report—— 跨 node cache 争用bpftrace tracepoint:sched:sched_migrate_tasknumastat -p <pid>taskset、numactl --physcpubind --membind
上一篇:SCHED_DEADLINE 下一篇:big.LITTLE / Intel P+E:异构多核调度
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。