第12讲 · 构建应用生态(Hacking Day)
2026 南京大学《操作系统原理》第十二讲 · 课堂实录重构
| 主讲 | 蒋炎岩(Yanyan Jiang) |
| 视频 | https://www.bilibili.com/video/BV17JDWBsEMi/ |
| 课程主页 | https://jyywiki.cn/OS/2026/ |
| 本讲讲义 | https://jyywiki.cn/OS/2026/lect12.md |
| 讲义 / 幻灯片作者 | 蒋炎岩,https://jyywiki.cn/ |
| 讲义 / 幻灯片许可 | CC BY-NC 4.0 |
| 图片来源 | 书中插图为课堂演示与讲义渲染图;演示类截图为视频画面 |
对应讲次:第12讲(南京大学 操作系统原理)
制作模型:deepseek-flash(coding-agent / pi;据 ~/.pi/agent/settings.json)
编者说明:本书由课堂录像的语音字幕重构而成,术语、章节骨架与参考链接已按课程官方讲义(
lect12.md)校准。讲师的口语风格、类比与案例被尽量保留;编辑性的补充一律以「编者说明」或「编者拓展」标出。
引子:虚拟化的最后一课
(参考时间: 00:00)
不知不觉讲到 Virtualization 部分的最后一节课了。今天的 Hacking Day 算是一个回顾:操作系统上的应用生态是怎么构建起来的。「今天大部分内容考试不会出现,但可能对大家比较有用。」
回顾虚拟化讲过什么:从程序和进程 → 进程的地址空间 → 文件描述符(访问操作系统对象)→ C 标准库的实现 → 上节课的链接和加载。
「其实整个学期到现在,我一直在讲系统调用」——上节课讲的链接和加载,本质就是 execve 系统调用的行为:把 ELF 文件的 PT_LOAD 段搬到进程地址空间、设置栈上的 argc/argv/envp/auxv、让 PC 指向 interp 的入口……「我提到的一切,你在 strace 里都能看到。」
课程目标:通过阅读手册、指导 AI 写代码,理解任何系统调用,并且弄清楚为什么要这样设计。「这么复杂的东西,其实概念上也没什么困难——你建立正确的概念,就能知道里面每一个字节是怎么来的。」
今天讲三件事:① 这些系统调用是怎么来的;② 系统调用来了以后怎么把应用生态「长」起来;③ 应用生态本身。
一、见证历史
1.1 Unix 的演化
(参考时间: 00:05)
Dennis M. Ritchie:Evolution of the UNIX Time Sharing System——强烈推荐每个人都读一读。它讲 UNIX 怎么从零一点点实现成今天这样:
- 最早的 UNIX 甚至没有
fork():shell 会关闭所有打开的文件,为 0、1 两个 fd 打开终端文件,从终端读命令行,打开文件、把加载器代码复制到内存并执行(相当于 exec),而 exit 会重新加载 shell。 - 一台终端里最多只有两个程序:shell 和可执行文件,来回切换;
- Ken Thompson 用 27 行汇编实现了
fork——因为早期内存模型极简,几个指针就能描述代码/数据段。
编者拓展|「从零开始」的启示:讲师说,所有复杂的东西在刚开始时都很简单。今天的大模型很可怕,但往前溯源,它们的雏形都很简单(就像并发课上那个能推理 GPT-2 的
gpt.c)。「Takeaway:不要害怕『不好』,大胆去做,并且持续改进。」
1.2 Minix:我「梦开始的地方」
(参考时间: 00:08)
Andrew S. Tanenbaum 写了 Minix:
- Minix 1(1987):UNIX v7 兼容,也是 Linus 实现 Linux 的起点;
- Minix 2(1997):POSIX 兼容,随书送代码(那本《Operating Systems: Design and Implementation》);
- Minix 3(2006):POSIX/NetBSD 兼容,全功能。
📷 Minix 1.7.5 在 QEMU 上启动:16-bit protected mode,如假包换的 UNIX v7 体验
回到原视频 (00:13:00)
编者拓展|Minix 3 是「应用最广的操作系统」:讲师讲了个响亮的故事——Intel ME(管理引擎) 里那个独立的芯片 CPU 上跑的操作系统就是 Minix 3(因为它的许可证很宽松)。所以「每一台英特尔芯片组电脑里都有一个 Minix 3」,不管你实际用的是 Windows、Linux 还是别的系统。「Minix 3 一度是世界上应用最广的操作系统。」
Minix 体验有多完整?有 vi 编辑器、cc 编译器、man 手册(man 1 用户命令、man 2 系统调用、man 3 库函数)、cat | wc 管道、/usr/src 里全套内核与工具源代码——「你可以在 Minix 里改 Minix 的内核,然后自己编译、烧录、启动。」
📷 Minix 里连 `man` 手册都有——和今天的 Linux 手册一模一样
回到原视频 (00:16:00)
编者拓展|微内核(microkernel):Tanenbaum 在 1987 年就想做一个只有一个系统调用的操作系统——
send/receive。文件系统、CPU 管理、内存管理都变成运行在用户态的服务进程(「A 同学负责文件系统、B 同学管 CPU、C 同学管内存」),进程之间发消息。这在当时非常超前、也不被看好(性能太差)。Andy 一直说:「我做 Minix 是为了上课,性能不是我要管的。」——「但我觉得很快微内核可能就要复活了(比如鸿蒙)。」
1.3 Linux 的诞生(1991 年 8 月 25 日)
(参考时间: 00:20)
Hello, everybody out there using minix – I'm doing a (free) operating system (just a hobby, won't be big and professional like gnu) for 386(486) AT clones. This has been brewing since April, and is starting to get ready. —— Linus Torvalds(时年 21 岁)
发布在 comp.os.minix(「相当于百度贴吧」)。早期的 Linux 还依赖 Minix 的工具链(磁盘要格式化成 Minix 文件系统),上面跑着 GNU gcc、bash……
📷 comp.os.minix 上那条帖子:「just a hobby, won't be big and professional like gnu」
回到原视频 (00:23:00)
编者拓展|「合适的人 × 合适的时间」:讲师说,合适的人在合适的时间做成了事;但更有意思的是,更多「合适的人在不合适的时间做了合适的事」——比如 Frank Rosenblatt 的 Perceptron 论文(1958),受人类神经元启发、权重学出来,「这不就是今天的深度神经网络吗?」——但直到 2012 年才有 AlexNet。因为当时没有算力、也没有氛围,中间还经历了人工智能寒冬。「我们需要正确的时间、正确的地点、合适的人。」
1.4 名场面:质疑、回应与时代的车轮
(参考时间: 00:25)
「The single worst company we've ever dealt with…」(2012):那年 Alex Krizhevsky、Ilya Sutskever、Geoffrey Hinton 还在搞 AlexNet,NVIDIA 在 2006 年推出了那些年「反人类」的 CUDA 编程模型,Linus 在邮件列表里对 NVIDIA 竖了中指——天道好轮回:「能想象 NVDA 是现在市值最高的公司?」
📷 Linus 与 NVIDIA 的名场面:当年「最烂的公司」,今天市值第一
回到原视频 (00:27:00)
在 comp.os.minix 上,关于 Linux 的讨论越来越多,Tanenbaum 做了「官方回应」(「To me, writing a monolithic system is a truly poor idea」),Linus 完全不服气(全文);已经得了图灵奖的 Ken Thompson 也来回帖:
Generally agree that microkernels are probably the wave of the future. However, in my opinion it is easier to implement a monolithic kernel… easier for it to turn into a mess in a hurry as it is modified. —— Ken Thompson
时代成就的英雄:Linux 0.3.0 引入多处理器(一开始是 Big Kernel Lock,内核不能并行),2.4 内核才真正并行,2002 年才引入 RCU(Read-Copy-Update)无锁同步,2003 年 Linux 2.6 发布后随云计算开始起飞。
📷 Linux 内核代码行数的增长:从 Hobby Project 到「泥潭」再到现代系统
回到原视频 (00:33:00)
编者拓展|「院士竟然可以下场来贴吧回帖」:讲师感慨,Ken Thompson(图灵奖得主,相当于「比中科院院士还高一级」)实名在论坛里回一个年轻人的帖子——「在我们这儿是一件政治不太正确的事……但如果有一天真的可以变成这样,我就去贴吧跟你们论战:你们写了个 CrazyOS,我就去跟你们互喷,喷完之后这个系统越变越好,那我觉得这绝对是值得的。」
1.5 同样的故事还在反复发生
(参考时间: 00:38)
- 一切伟大都「从零开始」:Fire in the Valley(个人电脑)、《Evolution of the UNIX Time Sharing System》;
- AI 时代,从 0 到 0.1 变得前所未有地容易:CrazyOS(可以和 hardware 做 co-design)、Claude「finding bugs is easy」、caveman(why use many token when few do trick)。
编者拓展|caveman 的「精妙之处」:它用极致的 prompt 压缩输出,利用了 agent 的容错性——话少了会偶尔做错,但 agent 有自主性,发现做错就再试一次。只要准确率下降不太多,总体就能省下大量 token。「这是从 0 到 0.1 前所未有地容易的一个侧影。」
在 AI 帮助下读完一篇论文:讲师现场演示自己的流程——把 PDF 丢进共享文件夹,后台自动 OCR,产出一句话摘要 / 一段摘要 / 一页纸三个级别。「我可以在几分钟内看完一篇一百多页的博士论文。」这在以前无论如何做不到。
二、创造世界:从 initramfs 点亮 Linux
2.1 关于「初始状态」
(参考时间: 00:46)
- 进程的初始状态:
execve(path, argv, envp)——SP 指向[argc, argv, 0, envp, 0, auxv],path 和 interp 被加载到内存,PC 是 interp 的 ELF entry; - 计算机系统的初始状态:CPU Reset(手册指定的行为)→ 运行 Firmware → 加载操作系统;
- 操作系统的第一个进程? —— 这个「长出」了整个操作系统世界的进程,它到底在哪里、做了什么?
「你只要能够问出这个问题:我们能不能控制 Linux 加载的第一个进程?—— 合理的事情就一定能做到。」
2.2 initramfs:最小的 Linux
(参考时间: 00:49)
我们写过最小的 x86-64 静态二进制——用系统调用打印一行、然后 exit:
; This is a Minimal Binary —— 不依赖任何库函数,直接做 write + exit 系统调用
📷 最小 x86-64 二进制:「This is a Minimal Binary」
回到原视频 (00:50:00)
编者说明:讲师指出,这样的二进制在 ARM 树莓派上也能运行——因为加载器知道系统里有 QEMU,遇到别的体系结构的二进制文件会进入用户态模拟:所有用户态指令(move、计算、内存)被模拟执行,而系统调用直接转发给宿主操作系统。
要控制第一个进程,就需要一个 initramfs(initial RAM filesystem):execve 的第一个参数是 pathname,指向系统里的一个文件——但内核刚起来时还不知道你的文件系统在哪,所以最早的「文件」必须放在一个你能控制的内存文件系统里。
「如果你用 apt update 更新软件包,会看到一个耗时特别长的操作叫 update-initramfs——就是因为有些软件更新后要被重新打回这个最初始的文件系统。」
Linux 内核硬编码了一系列路径,会逐个尝试 execve:
graph LR
K["Linux 内核完成最小初始化"] --> T{"依次尝试 execve"}
T -->|"/sbin/init 不存在"| T2["/etc/init"]
T2 -->|"不存在"| T3["/bin/init"]
T3 -->|"…"| T4["直到成功"]
T4 --> P["第一个进程诞生"]
P --> S["操作系统变成『系统调用提供者』"]
打包过程:把目录树用 CPIO 归档 + gzip 压缩成 initramfs.cpio.gz,QEMU 启动时用 -kernel 加载内核、-initrd 加载 initramfs,再加一个参数 rdinit=/init 覆盖内核默认行为。
📷 QEMU 启动:`Run /init as init process`,然后打印出「This is a minimal binary」
回到原视频 (00:56:00)
有意思的是:这个 minimal init 执行 exit 后,内核直接 panic——Attempted to kill init!——「因为你的 init 退出了,整个操作系统就挂掉了。」我们确实控制了 Linux 加载的第一个进程。
2.3 解开真实系统的 initramfs
(参考时间: 00:57)
「我们能解开当前系统的 initramfs 吗?当然可以!」讲师让 AI 去做,过程很有意思——AI 一度「翻车」:
- 它解出来只有 7.3MB 的文件,但
initrd有 23MB——不一致!「你只要发现有东西没解出来,就可以让它继续。」 - AI 于是开始读二进制、写脚本:「在 trailer 后面还有 19MB 的额外数据」——它找到了 gzip 压缩的第二段 CPIO(Linux 内核的复杂之处:
initrd概念上是一个目录树,但压缩文件可以分几段压缩)。
📷 用 AI 解包树莓派真实的 initrd:目录树结构很像你的 `/`
回到原视频 (01:00:00)
编者拓展|「Coding Agent 真正强大的地方」:讲师强调,整个过程中他对具体细节、工具、文件结构「一无所知」,他有的只是最基本的观察(7MB ≠ 23MB)。「AI 可以像人一样探索,只要有足够好的直觉。」解出来的 initrd 里有:启动脚本、NTFS 支持、键盘布局、字体配置、BusyBox 提供的用户空间工具、动态链接库、内核模块与固件驱动——「这就是你磁盘损坏时进入 initrd 模式还能得到命令行体验的原因。」
2.4 pivot_root:切换真实根文件系统
(参考时间: 01:03)
你确信这个 initramfs 是你系统的一部分(内核日志指向它),但你在系统里从没见过它——它到哪去了?
答案是:Linux 启动时,systemd 并不是真正的「一号进程」。在 initramfs 阶段的最后,系统会执行一个系统调用 pivot_root:
int pivot_root(const char *new_root, const char *put_old);
// Changes the root mount in the mount namespace of the calling process.
它重新创造以 systemd 为根的进程树。之后可以 umount 把 put_old 释放。「真实的 Linux:驱动加载、NetworkManager、tty 字体变化……都是在 switch_root 之后由 systemd 拉起的。」
📷 systemd 进程树:`pstree` 展示的整个用户态世界(systemd 是进程树的根)
回到原视频 (01:04:00)
2.5 亲手点亮
(参考时间: 01:06)
讲师准备的 initramfs 里只有 init 和 bin/busybox(外加必要的驱动)。init 脚本的魔法在于:
# initramfs 阶段的 init
#!/bin/busybox sh
...
for cmd in $(/bin/busybox --list); do
/bin/busybox ln -s /bin/busybox /bin/$cmd # 一个文件 = 全套命令
done
mount -t proc proc /proc # 创建 procfs 对象
mount -t sysfs sysfs /sys
insmod ... # 安装驱动
mknod /dev/... c/b major minor # 创建设备节点
exec /bin/busybox sh # 进入交互 shell
📷 手工点亮的起点:`find /` 只有 `/bin/busybox` 和一堆驱动模块
回到原视频 (01:08:00)
编者拓展|「计算机系统里没有任何魔法」:讲师强调,每一条命令背后都是系统调用——
busybox ln -s背后是fork+execve+ 符号链接系统调用;mkdir对应创建目录;mount -t proc把 procfs 的所有对象在/proc里创建出来;mknod创建设备文件,需要指定主设备号/从设备号(如/dev/random主 1 从 8、/dev/urandom主 1 从 9)。「你的/dev里每一个文件都是这样创建的。」
挂载磁盘:编辑 /dev/vda(virtio 虚拟磁盘)→ mount -t ext4 /dev/vda /mnt → 里面是另一个文件系统(含网卡驱动和另一个 init)。
📷 VGA 设备点亮:`Goodbye QEMU Console`,图形化终端上一个像素一个像素画出来
回到原视频 (01:16:00)
点亮 Linux 世界:执行 pivot_root 换根后,启动一个 HTTPD 在 8080 端口;QEMU 用 -net user,hostfwd=tcp::8080-:8080 做端口映射,于是在宿主机浏览器里 localhost:8080 就能访问虚拟机里的网页。
📷 端口映射彩蛋:宿主机 `localhost:8080` 访问虚拟机里的 HTTPD(首页 404,说明网络已通)
回到原视频 (01:18:30)
「这个 Linux 是真正点亮了。从 initramfs 里只有那一个文件,到加载驱动、pivot_root、启动 HTTPD,再到外面能访问网络服务——所有这些全部都是用系统调用实现的。」
2.6 故事的结尾:应用视角的操作系统
(参考时间: 01:21)
- 操作系统会到达一个确定的初始状态:
initramfs+/dev/console+execve(init); - 操作系统 = 对象 + API:所有其他对象(procfs、devfs……)都是系统调用创建和管理的;
- 进程管理:
fork,execve,exit,waitpid,getpid…… - 对象与访问:
open,close,read,write,pipe,mount,mkfifo,mknod,stat,socket…… - 地址空间管理:
mmap,munmap,mprotect,msync…… - 以及其他机制:
pivot_root,chmod,chown…… - Unix → Minix → Linux,到达成熟稳定的状态。「在 Linux API 上,没有什么东西是不能做的——大语言模型、agent、抖音、淘宝,全部都是在 Linux API 上做出来的。」
三、应用生态
3.1 是应用生态成就了操作系统的繁荣
(参考时间: 01:22)
厂商、个人开发者每天都在发布新的应用。操作系统不仅需要实现这套 API,还需要一套核心工具集来支撑它们:基本的运行库、coreutils、安装工具、系统管理工具……
- 前互联网时代:DOS/Windows 3.X/95 用软盘/光盘发行——双击安装、输入 CD-Key(「破解简直太容易了」);
- 互联网时代:AppStore、
apt、rpm、PyPI、npm、HuggingFace 🤗、ollama……
📷 应用分发生态的演进:从软盘/光盘到 AppStore / apt / npm / HuggingFace
回到原视频 (01:23:00)
3.2 例子:Debian
(参考时间: 01:23)
Our Mission: Creating a Free Operating System. The Debian Project is an association of individuals, sharing a common goal: We want to create a free operating system, freely available for everyone. Now, when we use the word "free", we're not talking about money, instead, we are referring to software freedom.
1998 年的 apt-get install firefox——跨时代的 Advanced Packaging Tool(「苹果 App Store 要到 2000 几年才出来」)。它有一整套开源软件供应链的管理流程:从 unstable(每天往 GitHub 交的代码)→ 测试分支 → 稳定分支。
📷 Debian 的软件供应链:从 unstable 到 testing 再到 stable
回到原视频 (01:24:00)
Debian 软件包(deb)就是一个压缩包:
| 组成部分 | 内容 |
|---|---|
control.tar.xz |
control 文件:Package、Source、Version、Architecture、Maintainer、Depends、Suggests、Section、Priority、Description…… |
data.tar.xz |
实际的文件(绝对路径) |
dpkg 可以安装 deb 包——它也是操作系统上的一个普通应用程序(使用系统调用完成「安装」功能)。
编者拓展|软件供应链的安全:安装一个 deb 包的流程是 Preinstall & Unpack → Configure → Triggers → Postinstall。讲师警告:
postinstallhook 能偷走你的一切——「最近 axios(每周下载量超 3 亿次)被投毒了。」这就是为什么「一个安全漏洞就能把整个系统拖垮」。
3.3 AI 时代:应用生态的变化
(参考时间: 01:25)
- 生态的关键是开发者,但 qualified 的开发者太少了——「大学四年都在写高血压代码?错误的设计 = 无法维护的泥潭」——课程的使命是让大家「见识」各种设计;
- 应用生态:繁荣还是消亡? 「OpenClaw 🦞 时代,『应用程序』会退化为『工具』和『服务』吗?GUI 会不复存在吗?」——A2UI(A Protocol for Agent-Driven Interfaces)、豆包手机、Qwen 应用。
📷 AI 时代的接口变化:应用会退化为「工具」和「服务」吗?
回到原视频 (01:25:30)
总结
- 见证历史:UNIX(Ritchie 的演化史、27 行汇编的
fork)→ Minix(Tanenbaum,微内核send/receive)→ Linux(1991 年 8 月 25 日,Linus 的「just a hobby」)→ 现代 Linux; - 同样的故事反复发生:一切伟大都「从零开始」;AI 时代从 0 到 0.1 变得前所未有地容易(CrazyOS、caveman);
- 创造世界:
initramfs提供了进程运行的初始状态;内核硬编码尝试/sbin/init等路径;pivot_root切换真实根文件系统;BusyBox + 系统调用手工「点亮」一个 Linux; - 应用视角的操作系统 = 对象 + API:所有对象都是系统调用创建和管理的;在 Linux API 上没有什么不能做;
- 应用生态:是应用生态成就了操作系统的繁荣;
apt(1998)、deb 包结构、开源软件供应链;软件供应链安全(axios 投毒)。 - Takeaway:从 UNIX 发展到 Linux,操作系统经历了漫长的演进。Linux 的「两面」是内核和发行版生态,而 initramfs 提供了进程运行的初始状态。 而精彩的故事,在系统调用的对象和 API 之上仍在延续。
附:官方参考与延伸阅读
本讲的讲义见课程主页,以下链接按书中出现顺序整理:
阅读材料:强烈推荐读 Ritchie 的 Evolution of the UNIX Time Sharing System;并在自己的 Linux 上动手解包 initrd、观察 pivot_root 前后的世界。
版权说明:本讲讲义与幻灯片作者为蒋炎岩,采用 Creative Commons BY-NC 4.0 许可,来源于 https://jyywiki.cn/OS/2026/。视频画面与字幕版权归原作者所有。本书为课堂内容的学习性重构。