Linux进程调度器通过分层调度域和pull/push机制实现多核负载均衡:在唤醒、定时器中断、空闲进出等关键路径触发,基于load_avg和缓存亲和性决策迁移,参数可调以平衡性能与开销。

Linux进程调度器在多核系统中通过动态迁移任务和均衡运行队列,确保各CPU核心负载相对均匀,避免部分核过载而其他核空闲。
负载均衡的触发时机
内核不是持续检查负载,而是按需在关键路径上触发均衡操作:
- 新进程唤醒时:尝试将进程放到当前CPU负载最低的空闲或轻载CPU上(wake_affine机制)
- 定时器中断(tick)中:周期性(默认每毫秒)检查本CPU运行队列是否过载,若发现明显不均,启动pull或push操作
- CPU空闲前(idle entry):主动向邻居CPU“拉取”(pull)一个可运行任务,避免立即进入深度睡眠
- CPU从空闲唤醒时(load balance on wakeup):检查是否有更合适的CPU可承载刚唤醒的任务
运行队列与调度域(sched_domain)结构
Linux将物理拓扑抽象为分层调度域,每一级对应不同粒度的负载共享范围:
- 最底层是SCHED_DOMAIN_CPU(单核),向上依次为SCHED_DOMAIN_CORE(同物理核的超线程)、SCHED_DOMAIN_PACKAGE(同封装/Socket)、SCHED_DOMAIN_NUMA(同NUMA节点)
- 每个CPU维护一套调度域树,均衡只在同级域内进行;跨NUMA迁移受严格限制(默认禁用远程内存访问代价高的迁移)
- 调度域定义了哪些CPU逻辑上“靠近”,决定任务迁移的开销阈值和频率(如package域比core域更少触发均衡)
核心均衡策略:pull 和 push
负载不均时,调度器主要通过两种方式再分配任务:
- Pull(拉取):空闲或轻载CPU主动从重载CPU的运行队列中迁移一个可运行进程。这是最常见、开销较低的方式,由idle或tick上下文发起
- Push(推送):重载CPU在自身tick处理中,将部分任务推送到当前空闲的邻居CPU。仅在满足一定过载条件(如rq->nr_running远超平均)时触发,且受迁移带宽限制
- 迁移决策基于load_avg(加权平均负载)而非瞬时runnable数,更平滑抗抖动;同时考虑缓存亲和性(cache_hot判断),避免频繁迁移导致TLB/CPU cache失效
影响均衡效果的关键参数
系统管理员可通过/sys/kernel/debug/sched_*/接口或内核启动参数微调行为:
- sched_migration_cost_ns:估算进程迁移带来的缓存惩罚,默认50万纳秒;值越大,越倾向保留进程在原CPU
- sched_nr_migrate:每次均衡最多迁移的任务数,默认32;增大可加快收敛,但增加开销
- sched_min_granularity_ns:调度周期最小粒度,影响负载统计精度;太小易抖动,太大则响应迟钝
- NUMA场景下,启用numa_balancing会额外跟踪内存访问模式,辅助进程与内存共置,但本身不属于传统CPU负载均衡范畴










