从零写 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) 。

关键步骤:

  1. BSP 解析 MP 表

    ,获取 CPU 核心数量和 LAPIC ID

  2. BSP 通过 LAPIC 发送 INIT/SIPI 消息

    唤醒 AP

  3. AP 从指定地址开始执行

    (通常是 0x10000)

💡 LAPIC(Local APIC) 是每个核心的中断控制器,也是核间通信的关键。


🔌 二、LAPIC 初始化与 AP 启动

  1. 启用 LAPIC

  2. 解析 MP Floating Pointer

  3. 启动 AP 核心


🧱 三、AP 启动代码(汇编)

AP 从实模式开始执行,需要:

  1. 设置保护模式
  2. 启用分页
  3. 跳转到 C 代码

🔑 AP 使用与 BSP 相同的 GDT 和页目录 (简化设计)。


🔒 四、核间同步:自旋锁与原子操作

多核并发访问共享数据时,必须同步!

  1. 原子操作(内联汇编)

  2. 自旋锁(Spinlock)

⚠️ 自旋锁持有期间必须关闭中断 (避免死锁)!


🔄 五、多核调度器改造

  1. 每个核心独立的运行队列

  2. 获取当前核心 ID

  3. 调度器函数改造

  4. 负载均衡(可选)

    定期检查其他核心的运行队列
    如果负载不均,迁移任务


📞 六、核间中断(IPI):核心间通信

LAPIC 支持 核间中断(IPI) ,用于:

调度请求

:一个核心唤醒另一个核心
TLB 刷新

:一个核心修改页表后通知其他核心
关机/重启

示例:TLB 刷新


🧪 七、测试:多核并行计算

用户程序:

内核:

启动 4 个进程,绑定到不同核心
观察 QEMU 的
-smp 4
输出

运行效果:

✅ 四个核心同时计算,总时间约为单核的 1/4!


⚠️ 七、多核编程的陷阱

  1. 伪共享(False Sharing)

    不同核心修改同一缓存行的不同变量
    解决方案:
    缓存行对齐

    attribute((aligned(64)))

  2. 内存屏障

    多核下 CPU 和编译器会重排序指令
    关键操作需加
    mfence
    /
    sfence
    /
    lfence

  3. 死锁

    多个锁的获取顺序必须一致
    使用
    锁层级
    (Lock Ordering)避免

  4. 中断亲和性

将设备中断绑定到特定核心,减少跨核同步

💡 多核编程的黄金法则:尽量减少共享状态,用消息传递代替共享内存!


💬 写在最后

多核支持是操作系统从“玩具”走向“生产级”的关键一步。 它不仅是性能的提升,更是 并发思维的革命 。

今天你唤醒的第二个 CPU 核心, 正是现代数据中心数百万核心并行计算的起点。

🌟 并行不是选择,而是必然——因为单核性能早已停滞。


📬 动手挑战 : 在 QEMU 中启动 4 核,创建 4 个计算密集型进程,验证加速比。 欢迎在评论区分享你的多核性能测试结果!

👇 下一篇你想看: 进程间通信(管道/Pipe) ,还是 信号(Signal)机制 ?


#操作系统 #内核开发 #多核 #SMP #LAPIC #调度器 #并行计算 #从零开始

最后修改: 2026年7月1日

作者

评论

发表评论

您的邮箱地址不会被公开。