操作系统|多处理机调度

前面我们学习的单 CPU 调度算法,只需要考虑怎么给单个 CPU 分配进程。而现在计算机普遍是多核 CPU,多处理机调度需要同时兼顾负载均衡处理机亲和性两大核心目标,衍生出「公共就绪队列」「私有就绪队列」两种经典实现方案。

一、多处理机调度的两大核心目标

我们先做整体类比: 多核 CPU = 公司里多个独立办公工位; 进程 = 需要办公的员工; Cache 高速缓存 = 工位桌面的临时文件收纳盒。

目标 1:负载均衡

尽可能让每一个 CPU 核心都保持忙碌,避免出现某个工位挤满人疯狂加班、其他工位长时间闲置没人干活的情况,最大化利用所有硬件算力资源。

举例:公司有 4 个工位,不能出现 3 个工位坐满员工高强度工作,1 个工位全天空着没人用的情况,要尽量把员工均匀分配到各个工位。

目标 2:处理机亲和性

尽量让同一个进程,始终固定在同一个 CPU 核心上运行,充分利用 CPU 内部的 Cache 高速缓存,减少数据重复加载带来的性能开销。

举例:员工第一天在 1 号工位办公,把常用资料、笔记都放在 1 号工位的桌面收纳盒里;如果第二天还安排他在 1 号工位,直接就能取用资料,效率很高;如果频繁把他换到 2、3、4 号工位,每次都要重新拷贝整理资料,会浪费大量时间。

简单总结两大目标的矛盾点:

  • 追求极致负载均衡,容易频繁迁移进程,破坏 CPU 亲和性;
  • 极致绑定 CPU 亲和性,又容易出现个别工位负载过高、其他工位闲置的不均衡问题。 多处理机调度,本质就是在这两个目标之间做权衡优化。

二、方案一:公共就绪队列(所有 CPU 共用同一个任务排队队列)

1. 方案原理

系统全局只维护唯一一条公共就绪队列,所有待运行的进程全部在这条队列里排队; 每一个 CPU 核心空闲时,都会主动去这条公共队列队首取出一个进程运行; 多个 CPU 同时访问公共队列时,必须加锁保证互斥,防止多个 CPU 同时抢到同一个进程引发数据错乱。

2. 优缺点分析

优点:天然实现负载均衡

哪个 CPU 先空闲,就会立刻去公共队列领取新任务,空闲工位会持续认领待办工作,不会出现某几个 CPU 长期闲置、某几个 CPU 满载的情况,算力天然均衡分配。

举例:公司所有员工统一在前台排队,哪个工位空了,前台就安排下一位员工过去办公,空闲工位永远优先接活,不会出现工位闲置。

缺点:进程频繁跨 CPU 迁移,处理机亲和性很差

进程本次在 CPU1 运行,时间片用完放回公共队列,下一次很可能被 CPU2、CPU3 调度执行,进程频繁在不同核心之间切换,之前 CPU 缓存里的数据全部失效,需要重新加载,严重降低运行效率。

举例:员工每次办完一件小事就回到前台重新排队,下次大概率被分配到全新工位,桌面的资料每次都要重新整理,办公效率大打折扣。

3. 如何提升公共队列下的处理机亲和性?

分为软亲和、硬亲和两种实现方式:

  1. 软亲和:由操作系统调度器做规则约束,尽量优先把进程调度回上一次运行的 CPU,只是倾向性建议,不强制绑定,系统负载过高时依然可以跨 CPU 迁移。

    例子:前台尽量优先把员工安排回上一次办公的工位,工位忙不过来时再调配去其他工位。

  2. 硬亲和:用户通过程序系统调用,手动指定进程只能固定在某一个或某几个 CPU 核心上运行,操作系统必须严格遵守绑定规则,绝对不允许跨 CPU 迁移。

    例子:员工向行政申请,固定只在 2 号工位办公,无论其他工位多空闲,都不会再被调配走。

三、方案二:私有就绪队列(每个 CPU 独立拥有专属就绪队列)

1. 方案原理

每一个 CPU 核心,都单独配置一条属于自己的私有就绪队列; 新进程创建时,会被分配到其中某一个 CPU 的私有就绪队列排队; CPU 只会从自己专属的就绪队列里挑选进程运行,默认不会去其他 CPU 的队列抢任务。

2. 优缺点分析

优点:天然具备优秀的处理机亲和性

进程自始至终都在同一个 CPU 的私有队列里排队运行,几乎不会跨 CPU 迁移,CPU Cache 里的临时数据可以反复复用,极大提升程序运行效率。

生活化例子:每个工位都有专属的排队员工,员工从头到尾只在这个工位办公,桌面资料可以反复使用,不用频繁迁移整理。

缺点:很容易出现负载不均衡

如果某个 CPU 的私有队列堆满大量进程,持续高负载;而其他 CPU 的私有队列任务很少甚至为空,就会出现部分核心忙、部分核心闲的资源浪费问题。

3. 私有队列如何实现负载均衡?

系统会周期性执行负载检测,通过 ** 推迁移(Push)、拉迁移(Pull)** 两种方式,在不同 CPU 之间迁移进程,平衡各个核心的任务量。

  1. 推迁移(Push 推送式) 系统定时巡检所有 CPU 的负载,一旦发现某个 CPU 负载过高、队列任务堆积严重,就主动从这个繁忙 CPU 的就绪队列里,挑选一部分进程,推送到空闲 CPU 的私有就绪队列中。

例子:行政定时巡查工位,发现 3 号工位排了十几个人,1 号工位没人,就主动从 3 号队列抽调几名员工,安排去 1 号工位办公。

  1. 拉迁移(Pull 拉取式) 当某个 CPU 空闲、自身私有队列没有待运行进程时,主动去其他高负载 CPU 的就绪队列里,拉取一部分进程到自己的队列执行。

例子:1 号工位员工全部办完,工位空闲,行政主动去排队人数最多的 3 号工位,抽调几名员工过来填补空闲工位。

四、两种调度方案对比表

对比维度 公共就绪队列 私有就绪队列
亲和性表现 差,进程易跨 CPU 迁移;可通过软硬亲和优化 天然优秀,进程固定在单一 CPU 运行
负载均衡 天然均衡,空闲 CPU 自动领取任务 容易失衡,需要推拉迁移手动调平负载
并发锁开销 高,多 CPU 访问全局队列必须加锁互斥 低,各 CPU 操作自身私有队列无需全局锁
典型适用场景 短期大量突发任务、追求极致算力均衡 长期稳定运行的服务进程、高性能业务(服务器主流方案)
优化手段 软亲和、硬亲和绑定 CPU Push 推迁移、Pull 拉迁移做负载均衡

五、总结

  1. 多处理机调度两大目标:负载均衡(不让 CPU 闲置)、处理机亲和性(复用 CPU 缓存,减少进程跨核心切换),二者存在一定取舍矛盾。
  2. 公共就绪队列:全局单队列,均衡性好、亲和性差,通过软 / 硬亲和优化缓存命中率;多 CPU 访问需要加锁,存在锁竞争开销。
  3. 私有就绪队列:每个 CPU 独立队列,天然亲和性好,容易负载失衡,依靠推迁移、拉迁移两种方式实现负载均衡,是当前服务器操作系统主流调度方案。

记忆短句

多核调度两目标,均衡亲和要协调; 公共队列均衡好,频繁换核亲和糟,软硬绑定来优化; 私有队列缓存妙,忙闲不均要微调,推拉迁移来找平。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐