南京大学 操作系统 (JYY) 学习笔记:从 UNIX 到 Linux 与庞大的应用生态
写在前面:这是本系列的第十二篇。
我们已经了解了一个典型的操作系统给应用程序提供的“一切”:从操作系统为应用程序提供的对象和 API,到可执行文件的链接和加载。操作系统在底层实现了管理硬件资源的极致机制,这是狭义“操作系统”负责的功能。
本讲内容:一个真实的“操作系统”,究竟是如何使用这些底层的对象和 API,一步步构建出今天如此丰富繁荣的应用生态的?在这个过程中,又有哪些不可思议的实用工具起到了决定性的作用?
课前反思:操作系统的对象
回顾:操作系统的对象
- 进程:进程 = 状态机。进程管理 API:
fork,execve,exit。 - 连续的内存段:可以被看作一个能在进程间共享、或映射到文件的对象。内存管理 API:
mmap,munmap,mprotect,msync。
从 UNIX 到 Linux 的历史回眸
Evolution of the UNIX Time Sharing System
- 最早的 UNIX 版本甚至没有
fork!- Shell 首先关闭所有打开的文件,然后为 0, 1 fd 打开终端文件(当时的系统是单进程的,根本不支持多进程)。
- 从终端读取命令行。
- 打开文件,把加载器代码复制到内存并执行 (相当于后来的
exec)。当时还没有fork()这个概念。 exit会重新把 shell 加载回内存。
Takeaway message: 不要害怕“不好”,大胆去做,并且持续改进!
- 先做一堆“垃圾”出来。
- 你见过的所有伟大系统,一开始都只是几个大学生、几个好朋友在车库里组队搞出来的破烂代码……
- 但只要方向对了,最后就成功了。从零开始做,比什么都重要。早期版本的操作系统其实和我们的 MiniLab 极其相似。
Minix:年轻人的第一个操作系统
- Minix1 (1987):UNIX v7 兼容。它是 Linus 后来实现 Linux 的起点。同时兼容 16-bit 和 32-bit,向世界真实展示了 16-bit 编程。
- Minix2 (1997):POSIX 兼容。作为微内核教学系统,源代码很简单,甚至可以直接印在书的附录里随书附赠。这时候它已经能实现现代 OS 的常用功能了。(Tips:Minix 是 32 位的,意味着
sizeof(int) == 2在某些老编译器下的历史包袱) - Minix3 (2006):POSIX/NetBSD 兼容。甚至一度是世界上应用最广的操作系统(因为被 Intel ME 偷偷内置在无数主板底层里)。
August, 1991: The Birthday of Linux
“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 岁)
- 这封邮件类似于在说:“我写了个加强版的 OSLab,现在发到贴吧(comp.os.minix)与大家分享”。
- 因为从零开始写全套工具是不现实的,所以早期的 Linux 还依赖 Minix 的工具链,跑的都是 GNU 的程序:
gcc,bash… - 天时地利人和:刚好踩中了 386(486) 的时间点,计算机硬件恰好在那时开始起飞。
“Just for fun”
“Revolutionaries aren’t born. Revolutions can’t be planned. Revolutions can’t be managed. Revolutions happen…”
—— David Diamond (《Just for Fun》作者)
- 对比当前教育部的政策(加强高校有组织科研、新型举国体制、在操作系统换道超车……)
- 在学校里总是被一些莫名其妙的事情拖住……因为我们的国家要生存,所以很多时候只能在夹缝中求生。但革命,往往是在“Just for fun”的不经意间爆发的。
“Linux is Obsolete” Debate 世纪大辩论
- 在
comp.os.minix论坛上,关于 Linux 的讨论盖过了原本的 Minix。 - 祖师爷 Andrew Tanenbaum 坐不住了,做出了“官方回应”,认为 Linux 采用宏内核(Monolithic)是“太落后”的设计。
- 21岁的 Linus 初生牛犊不怕虎,完全不服气,直接发长文回怼。
- 甚至连图灵奖得主、UNIX 之父 Ken Thompson 也参与了这场围观……
后来的事 (Linux 代码的指数级暴涨)
- Linux 2.0 引入了多处理器(但使用了 Big Kernel Lock,内核本质上不能并行)。
- Linux 2.4 真正实现了内核并行。
- 2002 年才引入了神级机制 Read-Copy-Update (RCU) 无锁同步。
- 2003 年 Linux 2.6 发布,随着云计算时代的到来,Linux 正式起飞,一统天下。
Linux 和应用程序的接口 🌶️
Linux 的“两面”
- Kernel (内核面):
- 负责加载第一个进程。这相当于在操作系统中“放置一个位于初始状态的状态机”。这就是大名鼎鼎的
initramfs模式。 - 维护并包含一些供进程操纵的操作系统对象。
- 除此之外,“什么也没有”。(在用户态看来,Kernel 其实就是一个躺在底层被动等待调用的 Trap Handler)。
- 负责加载第一个进程。这相当于在操作系统中“放置一个位于初始状态的状态机”。这就是大名鼎鼎的
- Ecosystem (发行版和应用生态面):
- 构建在 Linux Kernel 系统调用之上的系统工具 (
coreutils,binutils,systemd…)。 - 桌面系统 (Gnome, XFCE, Android)。
- 应用程序 (文件管理器, VSCode…)。
- 构建在 Linux Kernel 系统调用之上的系统工具 (
The last piece: “初始状态”
- Everything is a state machine!
- 既然是状态机,那“操作系统中的对象”必然需要一个初始状态。它是什么呢?
- 当你更新 Linux 系统时,经常会看到屏幕上卡在
update-initramfs... (漫长的等待)。没错,这就是 Linux Kernel 启动后的“初始状态”载体。
Initramfs:点亮 Linux 世界的第一把火
- 我们的 initramfs 实验:
里面可以只有一个最小的init文件(比如静态编译的极简 Linux 镜像),甚至可以是任何你自己写的一个二进制a.out。 - 系统启动后,Linux 还会为你增加
/dev和/dev/console(因为总得给stdin/stdout/stderr找个“输出的地方”)。 - 真实的 initramfs (你可以解压它看一看):
它包含基本的命令行工具、基础的设备驱动程序(以便随后能从真正的磁盘或网络上挂载庞大的文件系统),以及紧急文件系统修复工具(万一磁盘坏了,还能抢救一下)。
点亮世界的工具:Busybox
在真实的初始阶段,你看不到华丽的桌面,甚至没有完整的系统,只有一个busybox在默默支撑起早期的生命:
[ [[ acpid adjtimex ar arch arp arping ash awk basename bc blkdiscard blockdev brctl bunzip2 busybox bzcat bzip2 cal cat chgrp chmod chown chpasswd chroot...并不是我们平时“看到”的“Linux”世界
启动的初级阶段:
- 在 initramfs 中加载剩余必要的驱动程序(磁盘/网卡)。
- 挂载真实的根文件系统。Linux 内核有启动选项(类似环境变量),记录在
/proc/cmdline里。读取 root filesystem 的/etc/fstab。 - 神之一手:将控制权移交给另一个程序(例如现代 Linux 的霸主
systemd)。
启动的第二级阶段:
- 看一看系统里的
/sbin/init到底是什么? - 计算机系统没有魔法,一切的神奇都有底层系统调用的合理解释。
移花接木:构建真正应用世界的系统调用
switch_root命令背后的核心系统调用是pivot_root():
intpivot_root(constchar*new_root,constchar*put_old);- 这个系统调用会改变调用进程所在 mount namespace 的根目录挂载点。
- 它把当前的根挪到
put_old目录下,并把new_root提拔为新的系统根目录! - 在此之后,早期的那个用来过渡的
Initramfs就会被释放,功成身退。 /sbin/init指向的程序(比如systemd)开始接管一切,加载网卡、配置 IP、修改 tty 字体……真正的操作系统世界就此展开。
终局:操作系统 = 对象的集合与生态的繁荣
总结:什么是操作系统?
- 初始状态:
initramfs中的对象 +/dev/console+ 加载的init。 - 状态迁移:硬件选择一个进程(对象),执行一条指令。
- 系统调用支撑一切:
- 进程管理:
fork,execve,exit,waitpid… - 文件对象访问:
open,close,read,write,pipe,mount… - 地址空间管理:
mmap,sbrk… - 特权控制机制:
pivot_root,chmod,chown…
操作系统是幕后的 MVP
仅靠“初始状态 + 系统调用”这些机制,操作系统完全“感知不到”上层应用程序的存在。它只是无私地奉献着底层机制。
(嘿!也许在 AI 时代,操作系统可以通过为每一个 system call 甚至 function call 提供 traceability,从而真正去“感知”并理解应用的意图了!)
应用生态:从光盘到开源包管理器
- 前互联网时代:软盘发行 (DOS) -> 光盘发行 (Win95)。双击安装,输入 CD-Key,当时要破解简直太容易了。
- 应用商店模式:OS 提供 SDK/API,开发者独立打包发行,提交审核(如 AppStore 或 Microsoft Store,交 $19 保护费开个账号)。
- 开源模式 (Debian):开发者共同维护代码和应用仓库。
“We want to create a free operating system, freely available for everyone… we are referring to software freedom.”
—— Debian Project
计算机科学与其他学科截然不同的一点就在于:开源开放。
1998 年诞生的apt-get install firefox,是跨时代的“Advanced Packaging Tool”,彻底重塑了软件的分发模式。
Debian 的包管理与“软件供应链”:
一个.deb软件包,本质上只是一个压缩包:
control.tar.xz: 包含元数据(维护者、依赖 Depends、描述 Description…)data.tar.xz: 包含实际要解压到绝对路径的各种二进制文件。dpkg: 安装工具,其实它也只是操作系统上的一个普通应用程序,利用简单的文件读写系统调用完成“安装”。
为什么建设我们国家自己的操作系统生态那么艰难?
- 生态的关键是开发者。
- 但是 qualified(合格的)开发者太少了!大学四年都在写高血压代码,不懂底层原理。
- 错误的设计 = 无法维护的泥潭。
- 这门《操作系统》课程的终极使命,就是让大家“见识”各种优秀与糟糕的设计,从而写出真正高质量的系统级代码。
- 蒋门万岁!!!
AI 时代,"软件生态"会消失吗?
也许未来,我们只需要一个“最小”的执行环境,剩下的庞大包管理和组件,全部由 AI 随时随地按需生成。
篇末结语 (Virtualization 虚拟化篇 完结)
Take-away Messages:
至此,我们终于完全展示了逐层抽象的计算机系统世界:
- 最底层,硬件向上提供了指令集体系结构 (ISA)。
- 基于指令集,我们实现了操作系统(提供了各种对象、Syscall API,以及由 initramfs 定义的初始状态)。
- 操作系统之上,支撑起了基础的系统工具(
coreutils,shell,apt,gcc…)。 - 在系统工具之上,才是各式各样繁荣的应用程序。
实际上,我们在计算机系统中看到的五彩斑斓的画面,全都是由应用程序“完成”的,操作系统只是默默退居幕后,提供着极其原始的系统调用接口。
正是因为这些系统调用接口异常稳定、向后兼容,经过几十年的演化和积累,形成了难以逾越的技术屏障。在颠覆性的技术革新到来之前,任何想要“另起炉灶”做新操作系统的尝试,都注定是极其困难的。
