操作系统学习笔记
文章目录
0. 学习目标
这篇文章只围绕 JYY 的《操作系统:设计与实现》来安排学习,不额外展开嵌入式 C、外设驱动、RTOS、嵌入式 Linux 等独立路线。我的目标是用这门课建立操作系统的底层视角:
- 理解操作系统如何把硬件抽象成程序可使用的对象
- 理解并发、虚拟化、持久化三条主线
- 能读懂并实现小型内核中的关键机制
- 把课程实验和嵌入式方向关联起来:裸机、内存、中断、线程、调度、设备、文件系统
课程入口:
1. 课程前置要求
JYY OS 对前置能力要求比较硬。正式开始前,至少要能做到:
- 熟练写 C 语言代码
- 能使用 Linux 命令行
- 会使用
gcc、make、gdb - 理解指针、栈、堆、结构体、函数指针
- 知道程序从源代码到可执行文件的大致过程
- 对一种指令集有基本认识,例如 x86、MIPS 或 RISC-V
这里不单独开一条“嵌入式基础路线”。缺什么就在做 JYY OS 的过程中补什么。
2. 学习主线
JYY OS 的核心可以按四条线学习:
- 操作系统的整体模型
- 并发
- 虚拟化
- 持久化
对嵌入式方向来说,优先级可以这样看:
- 最高优先级:裸机运行环境、内存管理、中断、上下文切换、线程、同步互斥、设备驱动
- 中等优先级:进程、地址空间、系统调用、调度、文件系统
- 后续理解:xv6、Android、现代存储系统等更完整系统的实现
3. 第一阶段:操作系统整体认识
对应课程内容:
- 操作系统概述
- 操作系统上的程序
- 操作系统的状态机模型
- 状态机模型的应用
这一阶段先回答几个问题:
- 操作系统到底是什么?
- 程序为什么需要操作系统?
- 操作系统给程序提供了哪些对象?
- 硬件又给操作系统提供了哪些机制?
- 为什么操作系统可以被理解成状态机?
需要掌握:
- 进程、线程、地址空间、文件、设备这些抽象的意义
- API、系统调用、库函数之间的关系
- 程序执行状态如何变化
- 调试程序时如何观察状态变化
学习产出:
- 能用自己的话解释“操作系统是硬件和应用之间的抽象层”
- 能区分机制和策略
- 能解释为什么 OS 实验可以从一个普通 C 程序开始
4. 第二阶段:AbstractMachine 与裸机程序
对应课程内容:
- AbstractMachine
- L0: amgame
这是最贴近嵌入式的一部分。AbstractMachine 可以理解成一个简化的硬件抽象层,它让我们可以在裸机环境下写 C 程序。
需要重点理解 AbstractMachine 的几组 API:
- TRM:最基础的计算、输出和停机
- IOE:输入输出设备管理
- CTE:中断和异常
- VME:虚拟内存管理
- MPE:多处理器支持
学习重点:
- 什么是 bare-metal 程序
- C 程序如何不依赖完整操作系统运行
putch、halt这类最小接口的意义- 基础运行库
klib为什么重要 - 裸机程序中的
printf、memcpy、assert等函数从哪里来
建议实践:
- 阅读 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. 我的学习顺序
我打算按下面顺序推进:
- 课程概述、操作系统上的程序
- AbstractMachine 和 L0
- 并发部分和 M2
- L1 物理内存管理
- L2 内核线程管理
- 虚拟化:进程、地址空间、系统调用
- M3、M4、L3
- 持久化:设备、驱动、文件系统
- M5、L4
- xv6、Android 和课程总结
优先完成的实验:
- L0: amgame
- L1: pmm
- L2: kmt
- L3: uproc
- L4: vfs
优先完成的 mini labs:
- M2: libco
- M3: sperf
- M4: crepl
- M5: frecov
12. 每一章的记录模板
后续每学一部分,都按这个格式记录:
1 | ## 主题 |
13. 阶段验收标准
第一阶段验收:
- 能解释什么是操作系统抽象
- 能跑通 AbstractMachine 程序
- 能完成 L0
第二阶段验收:
- 能解释并发程序为什么会出错
- 能实现基本同步原语的使用
- 能完成 M2
第三阶段验收:
- 能实现物理内存分配器
- 能完成 L1
- 能说明分配器如何保证正确性
第四阶段验收:
- 能解释线程上下文切换
- 能完成 L2
- 能画出调度流程
第五阶段验收:
- 能解释系统调用和用户进程
- 能完成 L3
- 能说明程序如何被加载
第六阶段验收:
- 能解释设备驱动和文件系统 API
- 能完成 L4
- 能理解 VFS 的作用
14. 总结
这条路线只围绕 JYY OS 展开。对嵌入式方向来说,这门课最值得抓住的是:
- 裸机 C 程序如何运行
- 中断和异常如何进入内核
- 内存如何被管理
- 线程如何被创建和切换
- 并发如何被控制
- 设备如何被抽象
- 文件系统如何保存数据
先把这些学透,再去看 RTOS、驱动、嵌入式 Linux,会顺很多。