前面所有调度话题都假装只有一个 CPU.真机是 4 个 A53 加 2 个 A72,六个核,两种脾气,调度器的活立刻多了两件:任务放哪个核,核之间的负载怎么摆平.这篇把 SMP 的三层机制 — 亲和性,负载均衡,异构感知 — 全部在板上实测,顺手把频率联动也抓个现行.
这篇回答几件事:
- 任务怎么被摊到多个核上,谁在搬
- 绑核(亲和性)怎么玩,什么时候该绑
- 大小核的"大"和"小"到底差多少,调度器怎么利用这个差
- 调频和调度怎么联动
先看摊任务.起六个不绑核的死循环,两秒后查它们各自跑在哪个核 — /proc/PID/stat 的 psr 字段(第四十来列)就是任务最后所在 CPU,ps -eo comm,psr 直接看:
spin 0 / spin 1 / spin 2 / spin 3 / spin 4 / spin 5
六个任务恰好六个核,一人一个.这不是巧合,是负载均衡器在干活:每个 CPU 有自己的运行队列(所以叫 per-CPU rq),闲的队列会从忙的队列"拉"任务,周期性均衡加空闲时紧急均衡,搬的时机和数量由调度域的参数决定 — debugfs 里能看到几个旋钮,migration_cost_ns(搬任务的"运费",低于这个收益差的搬运不做)和 nr_migrate(一次最多搬几个).搬运不是免费的:任务换了核,它的 cache 全部作废,上一章说的切换成本之外又叠加一层,所以均衡器天生保守.
亲和性是用户态干预"放哪"的把手.taskset -c 4 ./prog 把任务钉在 cpu4,/proc/PID/status 的 Cpus_allowed_list 如实反映(板上默认 0-5 全开).什么时候该绑:性能抖动敏感的线程绑核避免迁移(cache 稳定);中断亲和和线程亲和配对绑同核(cache 命中);故意把测试负载和系统服务隔离.什么时候别绑:绑死妨碍均衡器救场,六个忙核的世界里把任务钉死等于自愿排队.
然后是这块板的重头戏,大小核.RK3399 是典型的 big.LITTLE:cpu0-3 是 A53 小核,最高 1.4GHz,cpu4-5 是 A72 大核,1.8GHz.同一个计算负载,钉在不同核上跑:
cpu0 (A53): 217 ms
cpu4 (A72): 86 ms -> 大核快约 2.5 倍
这不是频率差能解释的(1.8/1.4 只有 1.29),是微架构的差距 — A72 更宽的乱序窗口,更强的分支预测.1.5 篇提过设备树里每个 CPU 节点有 capacity-dmips-mhz,A53 是 485,A72 是 1024,启动时这些数字进了内核的拓扑表,归一化后就是调度器眼里的"每毫秒能干多少活".微基准测出 2.5 倍,DT 声明 2.1 倍,同方向不同值 — capacity 是个模型不是定律,具体负载的比值会飘,但它给调度器提供了统一的决策货币.
有了 capacity,SMP 调度的选核逻辑(select_task_rq)就从"挑最闲的"升级成"挑最划算的":唤醒一个任务,预估它的 util,能量感知(EAS)路径会比较把它放小核(省电但可能跑不完)和大核(快但费电)的代价,选满足性能前提下最省的;系统负载重到大核也挤,EAS 让路退回纯负载均衡.这套逻辑只有调度域按真实拓扑组织时才工作:每个 cluster 一个 MC 级域(共享 L2),往上一个 DIE 级域(整个 SoC),均衡在域内积极,跨域保守 — 谁都不愿意任务在两个簇之间来回横跳,跨簇的 cache 作废比簇内贵得多.
频率联动最后抓个现行.idle 时 cpu4 蹲在 600MHz(3.5 篇看的),六个 spin 压上去之后再看:
scaling_cur_freq = 1800000
满血.链路是调度器把每个任务的 util 信号喂给 cpufreq 的调度器调速器(schedutil),util 高说明忙,频率就该拉上去 — 调度和调频在 6.x 里已经是一对连体婴.这套反馈环也解释了 capacity 的另一个用途:util 是按"当前频率下的负载"算的,要换算成"满频需要多少"才能和 capacity 比,内核里这套换算贯穿 schedutil 和 EAS.
最后留几个容易忽略的细节:
- taskset 的掩码和 cgroup cpuset 是两套亲和来源,交集生效;容器里看到的"全部 CPU"往往已经是 cpuset 切过的子集.
- 负载均衡器救不了绑核失衡:六个任务全绑在 cpu0,均衡器一个都不会搬 — 你绑的,你负责.
- util 信号有个饱和问题(PELT 窗口 32ms 起步),突发负载的头几毫秒调度器是"盲"的,延迟敏感路径靠 uclamp 手动封顶托底.
- 大小核迁移有"升核容易降核难"的不对称:任务从小核搬大核立刻见效,反向搬容易让任务掉进性能坑,EAS 的代价模型专门防这个.
- 中断也有亲和(/proc/irq/*/smp_affinity),网卡中断和它的处理线程绑同核是网络调优的标准动作,纯负载均衡管不到中断.
- 热是终极约束:六个核全开满频,SoC 热到降频,你会看到"负载越高单核越慢"的反直觉曲线 — 调度器不管热,thermal governor 管它俩的接口在 cooling device.
- sched_isometric 一类的对称负载(比如视频转码开满)在小核上跑更省电:同样吞吐下小核的能效比常常更好,EAS 模型算的就是这笔账,微基准的快慢不等于全局最优.