Ch13 §03 操作系统 · 讲解稿¶
章节定位: Ch13 系统软件。教"OS 如何管理硬件": 进程/线程 / 虚拟内存 / 文件系统 / 调度 / 死锁。 承接: §02 硬件 → §03 OS 作为"硬件管家" 走向: §04 并发 (用多进程/线程) / §05 编程语言 篇幅: ~4200 字 / 阅读 12 分钟 / 讲解 25-30 分钟
一 · 一句话总览¶
操作系统 = 硬件与应用之间的"中间人": 进程 (运行单位) / 内存 (虚拟化) / 文件 (持久化) / 调度 (CPU 分配) / 死锁 (并发陷阱)。Linux 是 AI 工程师的事实标准。
二 · 心智模型¶
OS 像物业管理: - 进程: 住户 - 内存: 房间 - CPU: 公共设施 (轮流用) - 文件系统: 仓库 - 调度员: 决定谁用什么
三 · 章节主线 (5 段)¶
第 1 段 · 进程与线程 (10 分钟)¶
3.1.1 进程 (Process)¶
进程 = 一次程序的执行。有独立的地址空间 (~几 GB 虚拟内存)。
PCB (Process Control Block) 存: PID, 状态, 寄存器, 内存指针, IO 信息。
3.1.2 进程状态机¶
- 新建: 刚创建
- 就绪: 等待 CPU
- 运行: 占用 CPU
- 阻塞: 等待 IO
- 退出: 终止
3.1.3 线程 (Thread)¶
线程 = 进程内的执行流。共享地址空间, 独立栈 (1 MB)。
import threading
def worker(name):
print(f"Worker {name}")
t = threading.Thread(target=worker, args=("A",))
t.start()
t.join()
3.1.4 进程 vs 线程¶
| 维度 | 进程 | 线程 |
|---|---|---|
| 内存 | 独立 | 共享 |
| 创建 | 重 (~10 ms) | 轻 (~10 μs) |
| 通信 | IPC (难) | 共享变量 (易但需同步) |
| 崩溃影响 | 单进程 | 全线程 |
3.1.5 fork vs exec¶
import os
pid = os.fork()
if pid == 0:
# 子进程
os.execvp("ls", ["ls", "-l"])
else:
# 父进程
os.waitpid(pid, 0)
fork: 复制当前进程 (Code+Data)exec: 替换为新程序
第 2 段 · 虚拟内存 (10 分钟)¶
3.2.1 虚拟地址 vs 物理地址¶
程序用虚拟地址 (进程独立视角), CPU 通过 MMU 翻译为物理地址。
3.2.2 分页¶
- 内存分成固定大小页 (4 KB 典型)
- 虚拟页 → 物理页 (页表映射)
- 页表: 一项 = 4-8 字节
- 32-bit VA + 4 KB 页 → 1M 页表项 → 4 MB 页表
3.2.3 TLB¶
Translation Lookaside Buffer: 页表的硬件缓存。
- TLB 命中: ~1 ns
- TLB miss: 查页表 ~100 ns
- Page fault: 磁盘换入 ~10 ms
3.2.4 进程地址空间¶
0xFFFFFFFF ┌────────────┐
│ 内核区 │ (操作系统)
├────────────┤
│ 栈 ↓ │ (函数调用, 局部变量)
│ ⋮ │
│ 堆 ↑ │ (malloc/new)
├────────────┤
│ BSS │ (未初始化全局)
│ 数据段 │ (已初始化全局)
│ 代码段 │ (text)
0x00000000 └────────────┘
3.2.5 内存映射¶
import mmap
# 把文件映射到内存
with open('data.bin', 'rb') as f:
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
data = m[:100] # 直接读, 无 read() 系统调用
3.2.6 案例: vLLM PagedAttention¶
LLM 推理用 KV cache 存中间结果。显存有限, KV cache 大小不一, 浪费严重。
PagedAttention (vLLM, SOSP 2023): 像 OS 虚拟内存一样把 KV cache 分页, 减少碎片, 提升 4-24× 吞吐。
第 3 段 · 调度与死锁 (8 分钟)¶
3.3.1 调度算法¶
| 算法 | 思路 | 优缺点 |
|---|---|---|
| FCFS | 先来先服务 | 简单, 长作业卡短作业 |
| SJF | 短作业优先 | 最佳平均周转, 但饥饿 |
| RR | 时间片轮转 | 公平, 但切换开销 |
| CFS | Linux 完全公平 | 红黑树, \(O(\log n)\) |
| EDF | 最早截止优先 | 实时系统 |
3.3.2 死锁四条件¶
- 互斥: 资源独占
- 占有并等待: 持有 + 等
- 不剥夺: 不能强制释放
- 循环等待: 环路
打破任一即可。
3.3.3 避免死锁: 银行家算法¶
预先分配前, 检查是否安全 (无环)。
实用方案: 资源排序 + 一次性申请。
3.3.4 Python GIL¶
绕过 GIL: multiprocessing, numpy 内置并行, Cython, nogil Python 3.13+。
第 4 段 · 文件系统 (8 分钟)¶
3.4.1 文件系统结构¶
- 引导块 (boot)
- 超级块 (superblock): 文件系统元数据
- inode 表: 每个文件一个 inode, 存元数据
- 数据块: 实际内容
3.4.2 inode¶
inode = 256 字节, 存: 权限, 大小, 时间戳, 数据块指针 (12 直接 + 1 间接 + 1 双间接 + 1 三间接)。
3.4.3 常见 FS¶
| FS | 特点 |
|---|---|
| ext4 | Linux 默认 |
| XFS | 大文件, 高性能 |
| Btrfs | CoW, 快照 |
| ZFS | Solaris, 数据完整性 |
| NFS | 网络文件系统 |
| HDFS | Hadoop 分布式 |
3.4.4 数据湖与 LLM¶
LLM 训练数据存于: - S3 / GCS (对象存储) - HDFS / Ceph (分布式 FS) - 本地 NVMe (高速训练)
读取: mmap / 直接 IO / 异步。
第 5 段 · 中断与系统调用 (5 分钟)¶
3.5.1 系统调用¶
用户态 → 内核态的入口。
常用: open, read, write, fork, exec, wait, mmap, ioctl, socket。
3.5.2 中断 vs 异常¶
| 类型 | 触发 | 例子 |
|---|---|---|
| 中断 (IRQ) | 异步, 外设 | 键盘, 网卡 |
| 异常 | 同步, 程序 | 除零, 缺页 |
| 陷阱 (syscall) | 同步, 主动 | write |
3.5.3 上下文切换¶
进程切换保存: 寄存器, 程序计数器, 栈指针, 内存映射。
开销: ~1-10 μs。Linux 用 TLB shootdown 优化。
四 · 易错点 (5 条)¶
- 进程 vs 线程: 多线程不一定快, GIL 下 Python 多线程对 CPU 任务无效。
- 虚拟地址 ≠ 物理地址: 程序永远只看到 VA。
- 死锁必要条件: 满足不一定会死锁, 但不满足一定不死。
- Page fault 慢: ms 级, 应避免。
- inode ≠ 文件名: 文件名 → inode → 数据。
五 · 跨章连接¶
| 章节 | 关联 |
|---|---|
| Ch07 §04 Transformer | LLM 推理 KV cache 类似 OS 页表 |
| Ch08 §04 Diffusion | 数据加载 |
| Ch11 §05 VLA | ROS OS |
| Ch14 §04 图算法 | 邻接表 = 文件存储 |
六 · 自测 5 题¶
- 进程 vs 线程差别?
- 虚拟内存如何工作?
- 死锁四条件?
- 调度算法 FCFS / RR / CFS?
- inode 存什么?
七 · 一句话总结¶
操作系统 = 硬件与应用之间的中间人。进程 (运行单位) / 虚拟内存 (VA↔PA) / 文件系统 (持久化) / 调度 (CPU 分配) / 死锁 (并发陷阱)。Linux 是 AI 工程师的事实标准 (vLLM / Docker / K8s 都跑在 Linux 上)。
八 · 板书建议¶
[板书 1: 进程状态机]
新建 → 就绪 → 运行 → 阻塞 → 退出
[板书 2: 虚拟内存]
VA → MMU → PA
TLB 缓存页表
[板书 3: 地址空间]
高位: 内核
栈 ↓
⋮
堆 ↑
数据
低位: 代码
[板书 4: 死锁]
互斥 + 占有等待 + 不剥夺 + 循环
打破任一即可
九 · 参考资料¶
- Tanenbaum, "Modern Operating Systems"
- Linux Man Pages (
man 2 fork) - vLLM PagedAttention 论文 (SOSP 2023)
- Ch02 §04 矩阵论 (本套书)
- Ch14 数据结构 (本套书)