4. 多处理器调度算法:全局调度、分区调度、混合调度、负载均衡策略

多处理器调度,嗯,这话题我做了快十年了。说实话,单核时代那种「一个任务跑到底」的日子早就过去了。现在你随便打开一个嵌入式设备,里面可能藏着四核、八核甚至更多。怎么让这些核好好干活,不打架、不空转,这就是我们今天要聊的核心。

我个人习惯把多处理器调度分成三大流派:全局调度、分区调度、还有混合调度。别急,咱们一个一个说清楚。

4.1 全局调度(Global Scheduling)

全局调度,说白了就是「所有任务共享所有核」。系统里有一个全局的调度器,它手里拿着所有就绪任务,哪个核空闲了,它就挑一个最合适的任务扔过去。

核心思想: 任务可以在任意核上执行,也可以随时迁移到另一个核。

举个例子,你有个四核系统,跑了8个任务。全局调度器会动态地把这8个任务分配到4个核上。某个核忙不过来了?没关系,调度器会把任务挪到空闲的核上。

优点:

  • CPU利用率高,不容易出现「一个核累死,其他核闲死」的情况
  • 对任务数量不敏感,任务多了也能应付

缺点:

  • 调度开销大,每次任务迁移都要保存上下文、刷新缓存
  • 可调度性分析复杂,你很难证明所有任务都能按时完成

我在项目中遇到过一件事。有个视频处理系统,用了全局调度。刚开始跑得好好的,结果一上高负载,系统就开始丢帧。查了半天,发现是任务频繁迁移,缓存命中率掉到了30%以下。嗯,全局调度不是万能的。

我的建议: 全局调度适合任务数量多、但每个任务计算量不大的场景。比如物联网网关,处理各种小数据包,用全局调度很合适。

4.2 分区调度(Partitioned Scheduling)

分区调度就简单多了——每个任务固定分配到一个核上,一辈子不许动。你想想看,这就像公司里每个员工有自己的工位,不能随便换。

核心思想: 任务绑定到特定核,不允许迁移。

分区调度其实把多核问题转化成了多个单核问题。每个核上跑自己的任务集,用单核调度算法(比如RM、EDF)来管理。这样一来,调度分析就简单多了。

特性 全局调度 分区调度
任务迁移 允许 不允许
CPU利用率 可能不均衡
可调度性分析 复杂 简单
缓存性能 可能差
适用场景 任务多、负载动态 任务少、确定性要求高

分区调度有个大问题——负载不均衡。你想想,如果核0上绑了5个高负载任务,核1上只绑了2个低负载任务,那核0忙死,核1闲死。这就是所谓的「分区困境」。

避坑指南: 我曾经在一个航空电子项目中用了分区调度,结果有个核的利用率到了95%,另一个核才30%。后来我不得不手动调整任务分配,折腾了两周才平衡好。所以,分区之前一定要做好负载预估。

4.3 混合调度(Hybrid Scheduling)

混合调度,说白了就是「既要又要」。它把任务分成两类:

  • 关键任务: 用分区调度,固定绑定到核上,保证确定性
  • 非关键任务: 用全局调度,灵活迁移,提高利用率

这招我在自动驾驶系统里用过。控制算法(关键任务)绑定到专用核上,保证实时性;感知算法(非关键任务)用全局调度,充分利用剩余算力。

混合调度的核心原则:

  • 关键任务优先保证确定性,用分区
  • 非关键任务追求利用率,用全局
  • 两者之间要有隔离机制,防止非关键任务干扰关键任务

为什么会这样设计?因为实际项目中,你不可能所有任务都要求硬实时。有些任务晚几毫秒没关系,有些任务晚一微秒就出事故。混合调度就是给不同任务「区别对待」。

4.4 负载均衡策略

负载均衡,嗯,这才是真正的「艺术」。不管用哪种调度算法,最终目的都是让所有核的负载尽量均衡。

常见的负载均衡策略:

  1. 静态负载均衡: 编译时就分配好任务,运行时不变。简单,但不灵活。
  2. 动态负载均衡: 运行时监控每个核的负载,发现不平衡就迁移任务。灵活,但开销大。
  3. 半静态负载均衡: 定期检查负载,只在必要时迁移。折中方案。

我个人习惯用动态负载均衡,但会加一个「迁移阈值」。比如,只有当某个核的负载超过90%、另一个核低于50%时,才触发迁移。这样可以避免频繁迁移带来的开销。

一个小技巧: 负载均衡不只看CPU利用率,还要看缓存命中率、内存带宽、中断频率。我曾经遇到一个案例,两个核的CPU利用率都是70%,但一个核的缓存命中率是95%,另一个只有60%。结果那个缓存命中率低的核,实际性能差了一倍。

负载均衡的常用算法:

算法 原理 适用场景
轮询(Round Robin) 任务轮流分配到各个核 任务大小相近
最少连接(Least Loaded) 任务分配给当前负载最低的核 负载波动大
加权分配(Weighted) 根据核的性能权重分配任务 异构多核
预测分配(Predictive) 根据历史数据预测未来负载 周期性任务

你想想看,实际项目中很少只用一种策略。我通常的做法是:先用静态分配打好基础,再用动态均衡做微调。就像盖房子,先搭好框架,再装修细节。

注意: 负载均衡不是越频繁越好。我曾经在一个项目中把负载检查周期设成了1毫秒,结果系统50%的CPU时间都花在了负载均衡上,得不偿失。后来改成10毫秒检查一次,效果反而更好。

4.5 实际项目中的选择建议

说了这么多,到底怎么选?我给大家一个简单的决策流程:

  1. 先看任务数量: 任务少于核数?用分区调度,简单可靠。
  2. 再看实时性要求: 有硬实时任务?用分区调度或混合调度。
  3. 然后看负载变化: 负载波动大?用全局调度或动态负载均衡。
  4. 最后看硬件特性: 有共享缓存?注意任务迁移对缓存的影响。

我记得有个做工业控制的朋友,他问我为什么他的四核系统总是跑不满。我一看,他用的是全局调度,但任务都是周期性的、计算量固定的。我建议他改成分区调度,结果CPU利用率从60%提到了85%。

所以说,没有最好的调度算法,只有最合适的。你得根据实际场景来选。

总结一下:

  • 全局调度:灵活,但开销大,适合任务多、负载动态的场景
  • 分区调度:简单可靠,但可能负载不均,适合确定性要求高的场景
  • 混合调度:折中方案,适合既有关键任务又有非关键任务的场景
  • 负载均衡:所有调度算法都需要,但要注意频率和开销

嗯,多处理器调度这块内容不少,但核心就这些。下次你设计多核系统时,不妨先想想:你的任务是什么类型?你的硬件有什么特性?然后再决定用哪种调度策略。别一上来就抄别人的方案,那很容易翻车。