系统笔记
按概念组织的笔记,跨 lab 积累。
- 引用方向:系统笔记可以链接 lab 笔记(作为实践和例子);lab 笔记只为 lab 服务,不反向引用这里。
- lab 笔记记录:做什么、怎么做、踩了什么坑、测试结果。
- 系统笔记记录:这个抽象是什么、状态存在哪、为什么这样设计、内核里怎么实现。
目录
| # | 笔记 | 内容 | 主要来自 |
|---|---|---|---|
| 00 | 缩写与命名 | 怎么从名字猜全名;C/xv6/RISC-V/Linux 命令/构建缩写速查 | 全部 |
| 01 | 进程 | 用户态/内核边界与 syscall;fork、exec、wait;程序从 main.c 到进程 | util |
| 02 | 文件描述符与文件 | fd 是什么;目录与文件名;一条命令的完整一生 | util |
| 03 | 线程,以及 Python / C++ 里的进程与线程 | 进程 = 资源 + 执行流;调度与 swtch;Linux clone;C++ 数据竞争/atomic/mutex;Python GIL、multiprocessing | util + 宿主机实验 |
| 04 | 状态切换、权限与安全 | 特权级;ecall 进出内核时哪些状态变、谁改;用户输入的检查(copyin、TOCTOU);引用监视器与沙箱性质(seccomp 实验);资源复用前清零 | syscall + 宿主机实验 |
| 05 | 页表 | 为什么要地址翻译;Sv39 三级页表与一次完整翻译;PTE 各位;页表存在哪(直接映射);TLB 与 sfence.vma;xv6 两种地址空间;页表带来的能力(懒分配、COW、共享、保护页);A/D 位;大页的好处和代价(宿主机实测);Linux 对照 | pgtbl + 宿主机实验 |
| 06 | Linux 内核:定义、职责、心智模型与结构 | 八个角度的定义;14 项职责(抽象、核心结构体、源码、xv6 对应);七个心智模型(服务端、事件驱动、对象图、函数指针表、分层与机制/策略、缓存、并发)加宏内核与微内核;源码树地图与规模;task_struct;开机流程;Kconfig;本机观察;xv6 对照;开发流程与 AI 规定;读码方法 | 全部 + 本机源码树和运行中的内核 |
实验代码:code/(Python / C / C++,在宿主 Linux 上跑)。
后续 lab 的概念(页表、trap、调度、锁、文件系统……)按主题新增或并入已有篇目。
怎么建立系统概念的 mental model
跨 lab 的学习方法。写于 util lab 之后(2026-10-08)。
为什么 C++ / Python 的直觉"不管用了"
不是知识作废了,是视角变了。C++ 和 Python 讲的是"一个程序内部":对象、函数调用、调用栈,一条执行流从头走到尾。操作系统讲的是那个世界的外面:好几个程序同时存在,彼此隔离,中间有一个裁判(内核)。
而且旧知识恰好是这一层上面的封装:
| 熟悉的 | 底下其实是 |
|---|---|
Python f = open("x") |
内核给一个 fd(整数),文件对象只是包着它 |
f.close() / with open(...) |
close(fd) |
subprocess.run(["ls"]) |
fork + exec + wait |
subprocess.run("ls > out", shell=True) |
启动 sh,sh 做 fork → 重定向 → exec |
| C++ 段错误 | 访问非法地址 → CPU 异常 → 内核杀进程 |
print() |
最终是 write(1, ...) |
| 异常 | 系统调用没有异常,只有返回值 -1 |
Python 的 os 模块直接暴露了这些调用:os.fork()、os.execvp()、os.pipe()、os.dup2()。可以在宿主 Linux 上用熟悉的语言做实验。
一个核心模型:内核是服务端,进程是客户端
见 用户态与内核的边界。要点:状态存在内核里,用户手里只有一个编号(fd、pid)。想改状态只能发请求(系统调用),内核替你校验、替你办。
一个核心问题:状态存在哪、谁能改、每个操作对它做了什么
遇到新概念先问这三个问题,而不是先背 API。例如进程的两类状态:
| 进程自己的内存(代码、变量) | 内核替它保管的(fd 表、pid、当前目录) | |
|---|---|---|
| fork | 复制一份 | 复制一份(fd 指向同一批对象) |
| exec | 整个换掉 | 保持不变 |
| exit | 释放 | 全部关闭 |
记住这张表,重定向、管道、shell 的设计基本都能推出来。详见 fork 与 exec、fd。
哪些地方确实反直觉(单独记,不要硬套)
- fork 返回两次,exec 成功时不返回:它们不是普通函数,是"改变世界"的请求。
- 同一段代码同时有两份在跑:fork 之后有两条执行流。
- 一个整数代表一个资源:fd 3 在你这里是文件,在别的进程里可能什么都不是。
- 指针跨进程无意义:每个进程有自己的地址空间。
怎么练
- 先预测,再运行。跑之前写下预期输出;预测错的地方就是模型要修正的地方。
- 画框和箭头,不要从头到尾读代码。进程是框、fd 表是格子、内核对象在外面、箭头是指向。每执行一次系统调用,重画一次。范例:一条命令的一生。
- 只追一条具体路径,比泛读十个概念有效。
- 去看内核那一侧。xv6 的好处是神秘的东西只有几十行:fd 就是
struct proc里的ofile[16],fork 复制 fd 表就是proc.c里两行。 - 在宿主 Linux 上观察真实系统:
ls -l /proc/self/fd:直接看当前进程的 fd 表。strace -f sh -c 'echo hi > out':看到clone(Linux 的 fork)、close(1)、openat拿到 1、execve。strace python3 -c 'print(1)':print 最终就是write(1, "1\n", 2)。
- 接受第一遍是模糊的。课程是螺旋式的:util lab 用,后面的 lab 实现。很多概念要在内核里亲手写过一遍才会真正清楚。