0. 学习目标

这篇文章只围绕 JYY 的《操作系统:设计与实现》来安排学习,不额外展开嵌入式 C、外设驱动、RTOS、嵌入式 Linux 等独立路线。我的目标是用这门课建立操作系统的底层视角:

  • 理解操作系统如何把硬件抽象成程序可使用的对象
  • 理解并发、虚拟化、持久化三条主线
  • 能读懂并实现小型内核中的关键机制
  • 把课程实验和嵌入式方向关联起来:裸机、内存、中断、线程、调度、设备、文件系统

课程入口:

1. 课程前置要求

JYY OS 对前置能力要求比较硬。正式开始前,至少要能做到:

  • 熟练写 C 语言代码
  • 能使用 Linux 命令行
  • 会使用 gccmakegdb
  • 理解指针、栈、堆、结构体、函数指针
  • 知道程序从源代码到可执行文件的大致过程
  • 对一种指令集有基本认识,例如 x86、MIPS 或 RISC-V

这里不单独开一条“嵌入式基础路线”。缺什么就在做 JYY OS 的过程中补什么。

2. 学习主线

JYY OS 的核心可以按四条线学习:

  1. 操作系统的整体模型
  2. 并发
  3. 虚拟化
  4. 持久化

对嵌入式方向来说,优先级可以这样看:

  • 最高优先级:裸机运行环境、内存管理、中断、上下文切换、线程、同步互斥、设备驱动
  • 中等优先级:进程、地址空间、系统调用、调度、文件系统
  • 后续理解:xv6、Android、现代存储系统等更完整系统的实现

3. 第一阶段:操作系统整体认识

对应课程内容:

  • 操作系统概述
  • 操作系统上的程序
  • 操作系统的状态机模型
  • 状态机模型的应用

这一阶段先回答几个问题:

  • 操作系统到底是什么?
  • 程序为什么需要操作系统?
  • 操作系统给程序提供了哪些对象?
  • 硬件又给操作系统提供了哪些机制?
  • 为什么操作系统可以被理解成状态机?

需要掌握:

  • 进程、线程、地址空间、文件、设备这些抽象的意义
  • API、系统调用、库函数之间的关系
  • 程序执行状态如何变化
  • 调试程序时如何观察状态变化

学习产出:

  • 能用自己的话解释“操作系统是硬件和应用之间的抽象层”
  • 能区分机制和策略
  • 能解释为什么 OS 实验可以从一个普通 C 程序开始

4. 第二阶段:AbstractMachine 与裸机程序

对应课程内容:

  • AbstractMachine
  • L0: amgame

这是最贴近嵌入式的一部分。AbstractMachine 可以理解成一个简化的硬件抽象层,它让我们可以在裸机环境下写 C 程序。

需要重点理解 AbstractMachine 的几组 API:

  • TRM:最基础的计算、输出和停机
  • IOE:输入输出设备管理
  • CTE:中断和异常
  • VME:虚拟内存管理
  • MPE:多处理器支持

学习重点:

  • 什么是 bare-metal 程序
  • C 程序如何不依赖完整操作系统运行
  • putchhalt 这类最小接口的意义
  • 基础运行库 klib 为什么重要
  • 裸机程序中的 printfmemcpyassert 等函数从哪里来

建议实践:

  • 阅读 AbstractMachine 文档
  • 完成 L0 amgame
  • 补全一部分 klib
  • 跟踪一个裸机程序从编译到运行的过程
  • objdump 看生成的目标文件

嵌入式关联:

  • 对应 MCU 裸机开发中的启动环境、最小运行时、串口输出、基础库函数
  • 对后面理解 RTOS 移植和 BSP 很有帮助

5. 第三阶段:并发与同步

对应课程内容:

  • 多处理器编程
  • 理解并发程序执行
  • 并发控制:互斥
  • 并发控制:同步
  • 真实世界的并发编程
  • 并发 Bug 和应对
  • M2: libco

并发是嵌入式方向必须吃透的部分,因为中断、任务、DMA、驱动和 RTOS 都绕不开并发。

需要掌握:

  • 线程和执行流
  • 原子性
  • 数据竞争
  • 临界区
  • 互斥锁
  • 条件变量
  • 信号量
  • 自旋锁
  • 死锁
  • 并发 Bug 的复现和调试

重点问题:

  • 为什么一行 C 代码不一定是原子的?
  • 为什么关中断可以保护某些临界区?
  • 多核环境下为什么只关本地中断不够?
  • 锁保护的到底是代码,还是共享状态?
  • 同步和互斥有什么区别?

建议实践:

  • 完成 M2 libco
  • 写几个故意出错的并发程序
  • 用断言和日志观察线程交错
  • 对比 mutex、spinlock、semaphore 的使用场景

嵌入式关联:

  • 对应 RTOS 中的任务同步
  • 对应 ISR 和任务之间的数据共享
  • 对应驱动中的临界区保护
  • 对应多核 SoC 上的同步问题

6. 第四阶段:物理内存管理

对应课程内容:

  • L1: pmm

这部分是理解内核的关键。嵌入式系统里即使没有复杂虚拟内存,也必须管理有限的 RAM。

需要掌握:

  • 物理内存的表示
  • 空闲内存管理
  • 内存分配和释放
  • 对齐
  • 元数据管理
  • 分配器的正确性检查
  • 内存越界和 use-after-free 问题

重点问题:

  • 内核如何知道哪些内存可用?
  • 分配器如何记录空闲块?
  • 为什么内存分配需要考虑对齐?
  • 为什么内核里的内存 Bug 特别难调?

建议实践:

  • 完成 L1 pmm
  • 为分配器增加断言
  • 写压力测试反复分配和释放
  • 观察内存碎片

嵌入式关联:

  • 对应 RTOS heap 管理
  • 对应内存池设计
  • 对应 DMA buffer、任务栈、消息队列等内存资源管理

7. 第五阶段:内核线程与上下文切换

对应课程内容:

  • 操作系统上的进程
  • 进程的地址空间
  • L2: kmt
  • xv6 代码导读
  • 实现上下文切换
  • 处理器调度

这部分是从“裸机程序”进入“操作系统内核”的核心阶段。

需要掌握:

  • 线程控制块
  • 线程栈
  • 上下文
  • 中断和异常
  • 调度点
  • 上下文切换
  • 调度策略
  • 多处理器下的线程管理

重点问题:

  • 一个线程最少需要保存哪些状态?
  • 为什么每个线程需要独立的栈?
  • 中断发生时 CPU 状态如何保存?
  • 调度器什么时候运行?
  • 上下文切换为什么不是普通函数调用?

建议实践:

  • 完成 L2 kmt
  • 画出线程状态转换图
  • 跟踪一次上下文切换
  • 阅读 xv6 中的调度相关代码

嵌入式关联:

  • 对应 RTOS task control block
  • 对应任务栈初始化
  • 对应 PendSV/SysTick 触发调度的思想
  • 对应抢占式调度和协作式调度

8. 第六阶段:系统调用、进程与地址空间

对应课程内容:

  • 系统调用和 UNIX Shell
  • C 标准库的实现
  • A fork() in the road
  • 可执行文件
  • 可执行文件的加载
  • M3: sperf
  • M4: crepl
  • L3: uproc

如果目标是 MCU/RTOS,这部分可以先理解概念;如果以后做嵌入式 Linux,这部分非常重要。

需要掌握:

  • 用户态和内核态
  • 系统调用
  • C 标准库和系统调用的关系
  • 进程创建
  • 可执行文件格式
  • 程序加载
  • 地址空间
  • 用户进程管理

重点问题:

  • 应用程序为什么不能直接访问所有硬件?
  • 系统调用如何从用户态进入内核态?
  • libc 做了什么?
  • 可执行文件如何被加载运行?
  • 进程和线程的区别是什么?

建议实践:

  • 完成 M3 sperf
  • 完成 M4 crepl
  • 完成 L3 uproc
  • 阅读 xv6 的用户进程加载流程

嵌入式关联:

  • 对应嵌入式 Linux 的用户态/内核态分层
  • 对应 MPU/MMU 保护
  • 对应复杂系统中的应用隔离

9. 第七阶段:设备、驱动与持久化

对应课程内容:

  • 存储设备原理
  • 输入输出设备
  • 设备驱动程序
  • 文件系统 API
  • FAT 和 UNIX 文件系统
  • 持久数据的可靠性
  • M5: frecov
  • L4: vfs
  • xv6 文件系统实现
  • 现代存储系统

这部分和嵌入式里的设备、Flash、文件系统关系很密切。

需要掌握:

  • I/O 设备抽象
  • 驱动程序的职责
  • 块设备和字符设备的区别
  • 文件系统 API
  • FAT 文件系统的基本结构
  • UNIX 文件系统思想
  • 崩溃一致性
  • 虚拟文件系统 VFS

重点问题:

  • 操作系统如何把设备抽象成文件?
  • 驱动程序和应用程序之间如何隔离?
  • 文件系统如何组织磁盘或 Flash 上的数据?
  • 掉电时如何保证数据不损坏?

建议实践:

  • 完成 M5 frecov
  • 完成 L4 vfs
  • 阅读 xv6 文件系统实现
  • 对比 FAT 和 UNIX 文件系统的设计

嵌入式关联:

  • 对应 Flash 文件系统
  • 对应 SD 卡、eMMC、SPI Flash
  • 对应日志、参数保存、掉电保护
  • 对应设备驱动模型

10. 第八阶段:系统整合与回顾

对应课程内容:

  • 操作系统设计
  • 极限速通操作系统实验
  • Android 系统
  • 课程总结

最后阶段不急着追新内容,而是把前面的机制串起来。

需要回顾:

  • 一个程序如何从加载到运行
  • 中断如何进入内核
  • 线程如何被调度
  • 内存如何被分配
  • 设备如何被抽象
  • 文件系统如何保存数据
  • OS API 如何支撑上层应用

建议整理:

  • 画一张“硬件 - 内核 - 系统调用 - 应用”的结构图
  • 画一张“中断 - 调度 - 线程切换”的流程图
  • 画一张“内存分配器”的数据结构图
  • 画一张“文件系统读写路径”的流程图

11. 我的学习顺序

我打算按下面顺序推进:

  1. 课程概述、操作系统上的程序
  2. AbstractMachine 和 L0
  3. 并发部分和 M2
  4. L1 物理内存管理
  5. L2 内核线程管理
  6. 虚拟化:进程、地址空间、系统调用
  7. M3、M4、L3
  8. 持久化:设备、驱动、文件系统
  9. M5、L4
  10. xv6、Android 和课程总结

优先完成的实验:

  • L0: amgame
  • L1: pmm
  • L2: kmt
  • L3: uproc
  • L4: vfs

优先完成的 mini labs:

  • M2: libco
  • M3: sperf
  • M4: crepl
  • M5: frecov

12. 每一章的记录模板

后续每学一部分,都按这个格式记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
## 主题

### 1. 这一讲解决什么问题

### 2. 核心概念

### 3. 关键代码/实验

### 4. 我踩过的坑

### 5. 和嵌入式的关系

### 6. 尚未理解的问题

13. 阶段验收标准

第一阶段验收:

  • 能解释什么是操作系统抽象
  • 能跑通 AbstractMachine 程序
  • 能完成 L0

第二阶段验收:

  • 能解释并发程序为什么会出错
  • 能实现基本同步原语的使用
  • 能完成 M2

第三阶段验收:

  • 能实现物理内存分配器
  • 能完成 L1
  • 能说明分配器如何保证正确性

第四阶段验收:

  • 能解释线程上下文切换
  • 能完成 L2
  • 能画出调度流程

第五阶段验收:

  • 能解释系统调用和用户进程
  • 能完成 L3
  • 能说明程序如何被加载

第六阶段验收:

  • 能解释设备驱动和文件系统 API
  • 能完成 L4
  • 能理解 VFS 的作用

14. 总结

这条路线只围绕 JYY OS 展开。对嵌入式方向来说,这门课最值得抓住的是:

  • 裸机 C 程序如何运行
  • 中断和异常如何进入内核
  • 内存如何被管理
  • 线程如何被创建和切换
  • 并发如何被控制
  • 设备如何被抽象
  • 文件系统如何保存数据

先把这些学透,再去看 RTOS、驱动、嵌入式 Linux,会顺很多。