Appearance
第一章:操作系统概览
计算机由 CPU、内存和各种设备组成。应用程序希望运行代码、保存数据、读取文件、连接网络,并与其他程序同时运行。操作系统位于应用和硬件之间,为这些需求提供共同的运行环境。
操作系统的职责
CPU 与硬件设备千奇百怪,假如不存在操作系统,我们在裸机上编写一个简单的程序都非常复杂,甚至需要从引导、初始化键盘、屏幕开始写起。操作系统就是把这些共同需求集中实现,并向程序提供稳定的接口,比如我们可以通过目录系统来访问文件,而不需要知道其具体的存储硬件。
操作系统的职责可以先分为三类。
- 硬件抽象:不同型号的存储设备、网卡、输入设备(键盘、鼠标、触摸板等)具有不同控制方式。应用通常不直接操作具体设备,而是使用操作系统提供的设备接口。这使同一个程序可以在具有不同硬件的计算机上运行,只要操作系统提供兼容接口。
- 资源协调:程序会同时请求 CPU、内存和设备。操作系统记录这些请求和资源状态,并决定程序执行的顺序、轮转。第五章讨论 CPU 分配,第七章讨论设备请求。
- 程序隔离:程序可能包含错误,甚至可能包含恶意行为。操作系统需要限制程序能够访问的数据和资源,使一个程序的行为尽量不影响其他程序。第二章将讨论用户与内核的隔离限制怎样由 CPU 和操作系统共同实现。第十章进一步讨论用户程序的隔离和资源限制。
系统分层
操作系统最基本的结构可以表示为:
- 裸机:CPU、内存和各种设备硬件。
- 内核:管理程序、内存、文件、网络和设备,控制和协调硬件。
- 用户程序:应用程序,请求操作系统提供服务。
内核(kernel)是操作系统中负责管理硬件和共享资源的核心部分,也可以理解为一种特殊的直接工作于裸机之上的程序,通常在 CPU 中更高的特权级别运行。应用不能像调用自身函数一样随意进入内核内部,只能通过内核规定的接口请求服务。第二章将解释应用怎样进入内核提供的合法入口,以及内核怎样返回应用。
shell、C 标准库和图形界面通常运行在用户态,没有内核的全部权限。
程序执行
磁盘上的可执行文件只保存代码和数据。一个正在运行的程序,系统还需要记录:内存、文件、设备使用、执行状态等。
操作系统把一次程序执行及其相关状态组织为进程,或者说进程是操作系统分配资源的最小单位。一个进程还可以包含多个线程,线程是程序调度的最小单位。
第三章将正式定义进程和线程,并讨论程序怎样创建、替换和退出。第五章再讨论多个执行流怎样共享 CPU。
程序内存
程序员通常直接使用变量、数组、指针、堆和栈,不需要先询问其他程序正在使用哪些内存位置。多个程序即使使用相同的地址,也通常不会访问到相同的数据。
操作系统负责为用户准备隔离的内存区域。第四章将解释这种独立内存视图怎样实现。第六章讨论同一程序中的多个线程有意共享数据时,怎样保证结果正确。
文件系统
存储设备能够保存数据,但应用通常不希望直接管理设备上的位置。程序更习惯使用:
text
input.txt
documents/report.pdf
/home/user/config文件系统在存储设备之上提供文件和目录。它负责文件的增删查改、目录映射、元信息维护(大小、所有者、权限等)、落盘。
第八章将讨论数据从内存到存储设备的过程。第九章将讨论文件、目录和落盘语义。
网络功能
网卡负责发送和接收网络数据,但多个程序需要同时共享网卡。操作系统需要配置网卡、在程序与网卡间交换数据、实现通信协议、协调设备。
应用实现的功能与操作系统实现的功能并不完全相同。例如浏览器理解网页内容,Web 服务器理解 HTTP 请求;操作系统主要负责提供通用的网络通信能力,并协调网卡和多个程序。
第七章将从设备和异步 I/O 开始,再说明网络数据怎样从网卡到达应用。第十章会讨论容器为什么可以看到不同的网络配置。
设备等待
CPU 执行指令的速度通常远高于键盘、网络和存储设备完成请求的速度。程序等待数据时,如果继续占用 CPU 反复检查,会浪费可用于其他程序的执行时间。
操作系统可以暂停当前无法继续的执行流,让 CPU 运行其他程序。设备完成后,等待的程序再继续执行。这一过程涉及两类机制:调度和通知。第五章讨论调度和等待,第七章讨论设备怎样报告完成。
课程方法
本讲义以 RISC-V 上的 xv6 为机制主线。xv6 很小,但已经包含用户态与内核态、系统调用、页表、进程、调度、锁、设备驱动、日志和文件系统。每个核心抽象都能沿一条不长的源码路径读完。
各章采用同一顺序:
- 先说明 RISC-V 硬件在边界处自动完成什么;
- 再沿 xv6 源码看内核补完了什么;
- 由这条闭合路径提炼操作系统抽象;
- 最后扩展到 Linux、x86-64 和现代硬件中的复杂实现。
Linux 不是“更正确的答案”,xv6 也不是只供玩具实验的伪模型。前者展示规模、兼容性和性能约束,后者让核心机制保持可读。容器、eBPF、NVMe 等 xv6 没有实现的主题,则直接从其新增的边界出发,不强行寻找一一对应。
课程结构
- 第二章,受限执行:程序怎样在受到限制的情况下直接使用 CPU?
- 第三章,进程模型:一次程序执行包含哪些状态,程序之间怎样协作?
- 第四章,虚拟内存:为什么多个程序可以各自使用自己的地址?
- 第五章,CPU 调度:程序等待或同时运行时,CPU 怎样分配?
- 第六章,共享内存并发:多个核心同时访问共享数据时,怎样保证正确?
- 第七章,设备 I/O:操作系统怎样控制设备并收发网络数据?
- 第八章,存储数据路径:一次写入在到达存储介质前经过哪些阶段?
- 第九章,文件系统语义:文件和目录怎样组织,断电后如何恢复?
- 第十章,容器隔离:怎样限制一组程序能看到什么、能使用多少资源?
- 第十一章,虚拟机:怎样在一台机器上运行多个操作系统?
- 第十二章,内核可扩展性:驱动和新的内核功能可以放在哪里实现?
- 第十三章,全路径安全:各部分组合后还存在哪些安全、故障和性能问题?