从零写 OS 内核-第十八篇:多核支持 —— 让你的 OS 真正并行起来!
“单核调度只是伪并发,真正的性能飞跃在多核! 今天,我们唤醒所有 CPU 核心,实现 SMP(对称多处理)支持!”
在上一篇中,我们实现了 抢占式调度器 ,但所有任务仍在 单个 CPU 核心 上切换。 而现代处理器普遍是多核(双核、四核甚至更多), 真正的操作系统必须能 利用所有核心 ,实现 真正的并行计算 。
今天,我们就来: ✅ 解析多核启动协议(AP 启动流程) ✅ 实现核间通信与同步原语 ✅ 改造调度器支持多核调度
让你的 OS 在多核 CPU 上真正“火力全开”!
🧩 一、x86 多核启动基础:BSP 与 AP
x86 多处理器系统启动时:
BSP(Bootstrap Processor)
:第一个启动的核心(通常是 Core 0)
AP(Application Processor)
:其他核心,初始处于
休眠状态
启动 AP 的标准协议: MP Specification 或 ACPI MADT
为简化,我们使用 Intel MultiProcessor Specification (MP Spec) 。
关键步骤:
- BSP 解析 MP 表
,获取 CPU 核心数量和 LAPIC ID
-
BSP 通过 LAPIC 发送 INIT/SIPI 消息
唤醒 AP
-
AP 从指定地址开始执行
(通常是 0x10000)
💡 LAPIC(Local APIC) 是每个核心的中断控制器,也是核间通信的关键。
🔌 二、LAPIC 初始化与 AP 启动
- 启用 LAPIC
-
解析 MP Floating Pointer
-
启动 AP 核心
🧱 三、AP 启动代码(汇编)
AP 从实模式开始执行,需要:
- 设置保护模式
- 启用分页
- 跳转到 C 代码
🔑 AP 使用与 BSP 相同的 GDT 和页目录 (简化设计)。
🔒 四、核间同步:自旋锁与原子操作
多核并发访问共享数据时,必须同步!
- 原子操作(内联汇编)
-
自旋锁(Spinlock)
⚠️ 自旋锁持有期间必须关闭中断 (避免死锁)!
🔄 五、多核调度器改造
- 每个核心独立的运行队列
-
获取当前核心 ID
-
调度器函数改造
-
负载均衡(可选)
定期检查其他核心的运行队列
如果负载不均,迁移任务
📞 六、核间中断(IPI):核心间通信
LAPIC 支持 核间中断(IPI) ,用于:
调度请求
:一个核心唤醒另一个核心
TLB 刷新
:一个核心修改页表后通知其他核心
关机/重启
示例:TLB 刷新
🧪 七、测试:多核并行计算
用户程序:
内核:
启动 4 个进程,绑定到不同核心
观察 QEMU 的
-smp 4
输出
运行效果:
✅ 四个核心同时计算,总时间约为单核的 1/4!
⚠️ 七、多核编程的陷阱
- 伪共享(False Sharing)
不同核心修改同一缓存行的不同变量
解决方案:
缓存行对齐
(
attribute((aligned(64)))
) -
内存屏障
多核下 CPU 和编译器会重排序指令
关键操作需加
mfence
/
sfence
/
lfence -
死锁
多个锁的获取顺序必须一致
使用
锁层级
(Lock Ordering)避免 -
中断亲和性
将设备中断绑定到特定核心,减少跨核同步
💡 多核编程的黄金法则:尽量减少共享状态,用消息传递代替共享内存!
💬 写在最后
多核支持是操作系统从“玩具”走向“生产级”的关键一步。 它不仅是性能的提升,更是 并发思维的革命 。
今天你唤醒的第二个 CPU 核心, 正是现代数据中心数百万核心并行计算的起点。
🌟 并行不是选择,而是必然——因为单核性能早已停滞。
📬 动手挑战 : 在 QEMU 中启动 4 核,创建 4 个计算密集型进程,验证加速比。 欢迎在评论区分享你的多核性能测试结果!
👇 下一篇你想看: 进程间通信(管道/Pipe) ,还是 信号(Signal)机制 ?
#操作系统 #内核开发 #多核 #SMP #LAPIC #调度器 #并行计算 #从零开始
评论