1969 年夏天,贝尔实验室的肯·汤普森(Ken Thompson)发现了一台被闲置在角落里的老旧 PDP-7 计算机。他用了三周时间,在上面写下了一个简单的操作系统内核、一个 shell、一个汇编器。他的妻子和孩子出门度假,给了他这段不被打扰的时间。
这个周末项目就是 Unix 的雏形——今天地球上几乎所有重要的计算机系统(Linux 服务器、macOS、Android、iOS)都是它的后裔。
Unix 很快从 PDP-7 迁到 PDP-11。1970 年 5 月,汤普森和丹尼斯·里奇(Dennis Ritchie)以文本处理系统的名义说服管理层订购了这台新机器,真正要写的仍是操作系统。
破除误解:操作系统不只是"管理程序的程序"
操作系统通常被简单描述为"管理计算机资源的软件",但这太模糊了。更准确的理解是:操作系统是物理硬件与用户程序之间的抽象层,它把混乱的、物理的硬件世界转化成程序员可以使用的、整洁的虚拟世界。
没有操作系统,每个程序员都需要自己写代码来控制硬盘磁头的移动、管理 RAM 中每一个字节的分配、处理键盘中断……每个程序都要重新发明轮子,而且程序之间的相互干扰无法控制。
程序员真正碰到的操作系统接口,通常也不是桌面窗口,而是系统调用:open、read、fork、mmap。内核在这些入口上做权限检查和资源记账,再把工作派给驱动或文件系统。
操作系统的核心价值是三个抽象:
- 进程:把物理 CPU 抽象成"好像每个程序都有自己的专用 CPU"
- 虚拟内存:把物理 RAM 抽象成"好像每个程序都有连续的、私有的大内存"
- 文件系统:把物理磁盘(扇区、磁道、块)抽象成"文件和目录"这种人类可理解的结构
这三个抽象要成立,硬件必须提供特权级。用户程序跑在较低特权,只有内核能执行改页表、关中断、直接访问设备这类指令。
没有这条硬件边界,操作系统就只是一份普通程序,谁都可以绕过它。保护环与特权指令属于 计算机体系结构,操作系统只是把它们组织成可执行的政策。
历史:从"无 OS"到批处理到分时
1950 年代:裸机编程
早期计算机没有操作系统。程序员走进机房,把穿孔卡片送进去,计算机运行这一批任务,打印结果,换下一批。计算机一次只做一件事,期间不能被中断。CPU 大部分时间在等待慢速的 I/O(磁带、打印机)——资源浪费严重。
1960 年代:批处理 OS 与 Multics
批处理操作系统出现,自动加载下一个任务,减少空闲时间。IBM OS/360(1966)是第一个大规模商用操作系统,同时支持多种 IBM 机型——但它也以极端的复杂性和臭名昭著的 bug 载入史册(布鲁克斯定律——"增加人手只会让延误的项目更延误"——正是从 OS/360 的开发经历中总结出来的)。
Multics(1969)是 MIT、贝尔实验室、通用电气的合作项目,雄心勃勃地引入了时间共享(多用户同时使用一台计算机)、分段内存、多级目录等现代概念。Multics 太复杂了,最终失败;但它的失败直接催生了 Unix——汤普森和里奇决定做一个"简单版的 Multics"。
1969 年:Unix 的诞生
Unix 的设计哲学至今仍是现代操作系统的圣经:
- "一切皆文件"(Everything is a file):磁盘、键盘、打印机、网络接口,全部用统一的文件接口(open/read/write/close)访问
- "做一件事并做好它"(Do one thing well):小工具通过管道组合,而非一个大程序包揽一切
- 可移植性:1973 年,里奇(Dennis Ritchie)用 C 语言重写了 Unix 内核,使其能在不同硬件上运行——这是第一个主要用高级语言写的操作系统
内核从汇编迁到 C,杠杆其实是 编译器。目标机器上先有 C 编译器,再把内核源码重新编译,操作系统就能跟硬件解绑。1978 年,Unix 才第一次被移植到非 PDP-11 的 Interdata 8/32 上——C 重写五年之后。
迪克斯特拉 1968 年的论文《THE 多道程序系统的结构》("The Structure of the THE Multiprogramming System",CACM 1968)不仅描述了 THE 系统的实现,也从理论层面系统化了分层操作系统设计原则——THE 操作系统是第一个证明分层抽象结构可行的实验系统。
核心机制:进程管理
进程(Process) 是操作系统最基本的抽象单元。每个进程有: - 独立的内存空间(不能随便访问其他进程的内存) - 进程控制块(PCB):保存进程的状态、寄存器值、打开的文件等 - 至少一个执行线程
操作系统的调度器(Scheduler)决定哪个进程在何时使用 CPU。调度算法是一个经典的权衡问题:
| 算法 | 思路 | 优点 | 缺点 |
|---|---|---|---|
| FCFS(先来先服务) | 按到达顺序执行 | 简单 | 短任务等待长任务("护航效应") |
| SJF(最短作业优先) | 估计运行时间最短的先执行 | 平均等待时间最优 | 需要预知运行时间,长任务可能"饿死" |
| Round Robin | 每个进程轮流运行一个时间片 | 公平,响应时间有界 | 上下文切换开销,时间片选择困难 |
| 优先级调度 | 高优先级先运行 | 灵活 | 优先级倒置问题 |
| CFS(完全公平调度) | 红黑树 + 虚拟运行时间 | 长期份额公平 | 对唤醒延迟几乎不作承诺 |
| EEVDF | 合格任务中选最早虚拟截止时间 | 公平之外补延迟 | 实现与调参仍在演化 |
Linux 2.6.23(2007)把 CFS 做成默认调度器。6.6(2023)起由 EEVDF 接替同一套公平类:份额仍按虚拟时间记账,挑选规则改成"合格者中截止时间最早的先跑"。
上下文切换(Context Switch):从进程 A 切换到进程 B 时,操作系统必须保存 A 的所有寄存器状态,恢复 B 的状态。这有时间开销(几微秒),频繁切换会降低整体性能。
核心机制:内存管理
虚拟内存(Virtual Memory) 让每个进程"以为"自己拥有整个地址空间。32 位系统是 4GB;64 位在纸面上是 16EB,常见硬件实现只用 48 位规范地址,用户空间大约 128TB。物理 RAM 有限,不够的部分与磁盘(Swap)协同。
页表(Page Table) 维护虚拟地址到物理地址的映射。CPU 的内存管理单元(MMU)在每次内存访问时做地址转换:
虚拟地址 0x7fff1234
↓ MMU 查页表
物理地址 0x3a21234 ← 实际 RAM 中的位置
```页表由操作系统填写,地址转换由 CPU 的 MMU 完成。操作系统决定映射政策,硬件决定转换速度。二者缺一,虚拟内存就不存在。
如果访问的页不在 RAM 中(页错误,Page Fault),操作系统将其从磁盘加载——这对程序透明,但代价是巨大的延迟(磁盘访问比 RAM 慢 1000 倍以上)。
Swap 把磁盘当内存的延伸。一旦工作集放不进 RAM,系统就会把时间花在换页上而不是计算上——这就是抖动。它不是线性变慢,而是过了临界点之后突然不可用。
内存保护:虚拟内存的副产品——每个进程只能访问自己的虚拟地址空间,操作系统确保进程不能读写其他进程的内存。这是现代多进程系统安全性的基石。
核心机制:文件系统
文件系统把物理存储(磁盘块)抽象成用户熟悉的文件和目录结构。不同文件系统在性能、可靠性、特性上有不同的取舍:
| 文件系统 | 使用场合 | 特点 |
|---|---|---|
| FAT32 | USB 盘、早期 Windows | 简单,兼容性好,不支持大文件 |
| NTFS | Windows | 日志(Journal)、访问控制、压缩 |
| ext4 | Linux | 日志、大文件支持、性能稳定 |
| APFS | macOS | 写时复制(CoW)、快照、加密 |
| ZFS | 服务器 | 完整性校验、RAID 集成、无限快照 |
| BTRFS | Linux 服务器 | CoW、子卷、在线校验(类似 ZFS) |
inode、日志、写时复制的机制细节见 文件系统。这里只需记住一点:文件是操作系统对持久化的统一接口,磁盘、管道、套接字都可以长成"文件"的样子。
日志(Journaling):现代文件系统在实际修改数据前先写日志,若操作中途断电,可以通过日志恢复,避免文件系统损坏——这是从数据库事务概念借鉴的思想。
Linux:Unix 哲学的开源实现
1991 年 8 月 25 日,21 岁的芬兰学生林纳斯·托瓦兹(Linus Torvalds)在 Usenet 发了一条消息:
"我正在为 386(486) AT 克隆机做一个(免费)操作系统(只是业余爱好,不会像 gnu 那么大也那么专业)……"
这个"业余爱好"成了历史上最重要的软件项目之一。今天,Linux 内核: - 自 2017 年 11 月起,TOP500 超算榜上的系统全部运行基于 Linux 内核的操作系统 - 据 StatCounter 2026 年 7 月的统计,全球移动操作系统份额中 Android(以 Linux 为内核)约占 68% - 支撑着 AWS、Google Cloud、Azure 上的绝大多数服务器
Linux 成功的关键之一是开源模式:数以万计的志愿者贡献代码,由 Torvalds 和各个子系统维护者把关合并。这种去中心化的协作模式,本身就成为软件工程史上的一个重要实验。
微内核 vs 宏内核:永恒的争论
操作系统架构存在一个持续了几十年的争论:
宏内核(Monolithic Kernel):内核的所有功能(文件系统、网络、设备驱动)都在内核态运行,相互之间可以直接调用。优点:性能高(无需跨越内核/用户态边界)。缺点:一个 bug(如设备驱动崩溃)可能导致整个系统崩溃。Linux、传统 Unix 都是宏内核。
微内核(Microkernel):内核只保留最小功能(进程管理、内存管理、IPC),其他所有功能(文件系统、驱动)作为用户态服务运行。优点:更稳定(驱动崩溃不影响内核),更安全,更易验证。缺点:频繁的进程间通信导致性能开销。代表:MINIX、QNX、L4 系列。
1992 年,安德鲁·塔能鲍姆(Andrew Tanenbaum)与林纳斯·托瓦兹在 Usenet 上爆发了著名的辩论:塔能鲍姆声称 Linux 的宏内核设计是"从根本上错误的",将来会被微内核取代。三十年过去,Linux 的市场份额证明了宏内核的实用性;而 seL4(2009,经过形式验证的微内核)等项目则证明了微内核在安全关键领域的价值。
seL4 的关键不在口号,而在证明的范围。Klein 等人 2009 年在 SOSP 发表的工作,用定理证明器 Isabelle/HOL(不是 Coq)证明:约 8700 行 C 代码外加约 600 行汇编的 seL4 内核,功能上符合一份抽象规范。
配套经验报告给出的证明脚本超过 15 万行。证明仍假设编译器、汇编与硬件本身正确。所以内核验证并不自动包含 编译器 验证。
宏内核 Linux 的代码体量比这大出几个数量级。同等强度的全核证明至今没有。这不是微内核"赢了",而是可验证性对内核尺寸有硬约束。
Linux 之所以难被整核证明,不只是行数。宏内核把驱动、文件系统、网络协议和调度器放进同一地址空间,证明义务会随模块组合爆炸。微内核把这些推到用户态,内核的状态空间才小到能被定理证明器吃下去。
这个争论没有胜负,只有权衡:操作系统设计是工程与理论之间张力的永恒案例。
代价与争议
安全漏洞的系统性根源:操作系统的"内核态 vs 用户态"边界是最重要的安全边界,但这个边界反复被突破。2018 年公开的 Meltdown 和 Spectre 来自 CPU 的推测执行,几乎所有现代操作系统都受影响。
修复要在 CPU 微码和操作系统两边同时动手。开销并不固定:计算密集、很少进内核的负载几乎无感;系统调用频繁的负载上,2018 年前后的实测可以到百分之十几。把某一个固定百分比当成"Meltdown 税"并不准确。
实时性:通用操作系统(Linux、Windows)不能保证任务在确定的时间内完成——调度延迟是不确定的。这在工业控制、医疗设备、汽车系统中是致命问题。专门的实时操作系统(RTOS)如 VxWorks、FreeRTOS、RT-Linux 为此设计,但牺牲了通用性。
火星好奇号的飞行软件用的是 VxWorks,不是 Linux。通用内核把吞吐和兼容放在确定性之前;探测器着陆那几分钟,不能接受"调度延迟不确定"。
跨域连接
- 虚拟化与容器:三层假象用的是同一手法。进程让每个程序以为独占处理器,虚拟机让每个系统以为独占机器,容器让每个应用以为独占用户空间,差别只在被虚拟化的接口是指令集还是系统调用。这给出一条可推的规律:隔离强度随接口宽度递减,系统调用面比指令集宽得多,所以容器逃逸比虚拟机逃逸容易。
- 抽象:三个核心抽象都有各自的泄漏点,而泄漏处正是性能与正确性问题的聚集地。虚拟内存在页错误时露馅,一次访问从纳秒变成毫秒;文件接口套到网络设备上,阻塞语义与部分写就开始不合身。推论可以当评价标准用:一个抽象的价值,约等于它在多大比例的时间里不需要被戳破。
- 计算机体系结构:操作系统的每个抽象都坐在硬件原语上。进程切换要保存寄存器,虚拟内存要 MMU 和页表,用户态与内核态要特权级。Meltdown 一类漏洞说明:当微架构为了速度而推测执行时,架构说明书上的隔离可以被旁路。操作系统无法单方面修好硬件,它只能在已知的泄漏点上加垫片。
- 类型系统:类型检查在编译期排除一类错误;操作系统在运行期用地址空间和特权级排除另一类错误。二者都是"在越界发生前拦住",只是一个看程序文本,一个看内存访问。seL4 的 capability 把权限做成可传递的令牌,思路接近线性类型对资源的处理,但 2009 年那份证明是用 Isabelle/HOL 做的语义 refinement,不是靠编程语言的类型系统自动完成。
- 分配正义:调度算法的争论其实是分配原则的争论。最短作业优先让平均等待最优,却让长任务饿死;轮转公平,代价是上下文切换开销。更关键的是公平本身有多种不相容的定义——等份的时间与等份的进度并不等价。任何调度器都得先选定义再谈最优,虚拟运行时间就是一次这样的选择。
- 公共池塘资源治理:内核开发不是无政府,而是有边界、有分级把关、有冲突解决机制的自治秩序:子系统维护者负责合并,争议逐级上抬。这解释了一个反差——「谁都能直接提交」的项目通常在规模扩大后崩溃,而分层把关的项目能吸纳数以万计的贡献者。是治理规则而非工具,决定了协作能扩展到多大。
- 相变与临界现象:内存抖动与拥塞崩溃的共同点是正反馈:换页本身消耗产出所需的资源,于是负载越过某个阈值后性能不是线性下滑而是骤降。这类系统在临界点附近对参数极端敏感,测得的平均值几乎没有外推价值。推论是容量规划必须把工作点留在临界区之外,而不是逼近它去榨取利用率。
参考文献
- Ritchie, D. M. & Thompson, K. The UNIX Time-Sharing System. CACM 17(7), 1974.(Unix 原始论文)
- Dijkstra, E. W. The Structure of the "THE" Multiprogramming System. CACM 11(5), 1968, pp. 341–346.
- Klein, G. et al. seL4: Formal Verification of an OS Kernel. Proc. 22nd ACM SOSP, 2009, pp. 207–220. doi:10.1145/1629575.1629596.(Isabelle/HOL,非 Coq)
- Klein, G., Derrin, P. & Elphinstone, K. Experience Report: seL4 — Formally Verifying a High-Performance Microkernel. ICFP 2009.(约 8700 行 C、15 万行以上证明脚本)
- Brooks, F. P. The Mythical Man-Month: Essays on Software Engineering. Addison-Wesley, 1975.(OS/360 与布鲁克斯定律)
- Tanenbaum, A. S. Modern Operating Systems. 4th ed. Pearson, 2014.(最权威的教材)
- Love, R. Linux Kernel Development. 3rd ed. Addison-Wesley, 2010.(Linux 内核实现详解)
- McKusick, M. K. et al. The Design and Implementation of the FreeBSD Operating System. Addison-Wesley, 2014.