1969 年的一个夏天,肯·汤普森(Ken Thompson)的妻子和儿子去加利福尼亚度假,给他留下了三个星期的独处时光。他用这三周,在贝尔实验室一台被废弃的 PDP-7 小型机上,写出了一个操作系统内核、一个 shell 和一个文件系统编辑器。
这就是 Unix 的诞生。
这个故事因为过于传奇而常被质疑。汤普森本人后来确认了这个基本框架,但也强调实际工作远比"三周"复杂——这三周完成的是核心结构,完善工作持续了数年。然而关键是:Unix 诞生于一个人的专注、一台废弃的机器,以及一段意外的独处时光。
破除误解:汤普森不只是里奇的合作者
丹尼斯·里奇(Dennis Ritchie)发明了 C 语言,因此在公众认知中往往比汤普森更知名。但如果要区分两人的核心贡献,汤普森更像是 Unix 的架构师和最初实现者——是他在 PDP-7 上从零打出了第一行 Unix 代码,是他设计了 Unix 的文件系统哲学和进程模型。
里奇是语言的发明者和理论深度的提供者,汤普森是那个坐下来把事情做出来的人。
现场:从太空旅行游戏到操作系统
汤普森 1943 年 2 月 4 日生于新奥尔良,在加州大学伯克利分校获得电子工程学士和硕士学位,1966 年加入贝尔实验室。
1960 年代末,贝尔实验室曾参与 MIT 的 Multics 操作系统项目。Multics 野心勃勃,但进展迟缓,贝尔最终于 1969 年退出。退出之后,汤普森、里奇以及一批同事发现自己突然没有了可以使用的分时操作系统。
汤普森原本在 Multics 上开发一个叫"太空旅行"(Space Travel)的游戏——一个模拟太阳系行星运动的小程序。Multics 没了,他想把这个游戏移植到那台废弃的 PDP-7 上。但 PDP-7 上没有操作系统。于是他写了一个。
这个起点令人忍俊不禁:Unix 的最初驱动力,是为了跑一个游戏。
Unix 的核心设计
汤普森的 Unix 有几个当时极不寻常的设计决策,后来成为了操作系统设计的行业标准:
一切皆文件(Everything is a file):设备、进程、网络连接,都被表示为文件,通过统一的 open/read/write/close 接口操作。这极大简化了程序员的心智模型——学会了操作文件,就学会了操作一切设备。
管道(Pipes):程序的输出可以直接接入另一个程序的输入,用 | 符号连接。这个 1973 年由道格拉斯·麦克罗伊(Douglas McIlroy)完善的特性,使得数十个简单工具的任意组合成为可能,彻底改变了命令行交互的范式。
进程与分叉(Processes and fork):Unix 用 fork() 系统调用创建新进程——子进程是父进程的精确副本,然后两者分叉执行。这个模型简单、优雅,且极易理解,成为了后来所有 Unix 系衍生系统的基础。
Shell 脚本:Shell 既是命令解释器,也是一种编程语言。汤普森设计的第一个 Shell(Thompson Shell)奠定了 Bourne Shell、bash 等后续 Shell 的基础,也奠定了 DevOps 时代"脚本化一切"的文化。
B 语言:C 的前身
在 C 语言之前,汤普森发明了 B 语言。B 语言源自 BCPL(Martin Richards 设计),汤普森简化和修改了它以适应 PDP-7 的内存限制。B 语言是无类型的(所有值都是"机器字"),这在 PDP-7 上够用,但当他们将 Unix 移植到 PDP-11 时,需要区分不同大小的数据类型。于是里奇在 B 的基础上发展出了有类型系统的 C 语言。
汤普森→B 语言→里奇→C 语言,这条谱系清晰地说明了两人工作的递进关系。
正则表达式:从 QED 到 grep
在 B 语言之前,汤普森还留下了一项影响更隐蔽的发明:正则表达式的第一个工程实现。
1960 年代中期,他在 CTSS 分时系统上实现 QED 文本编辑器时,把数学家斯蒂芬·克莱尼(Stephen Kleene)1950 年代提出的正则表达式记号第一次搬进了实用程序。汤普森的实现有一个今天看来极具前瞻性的技巧:他不解释执行匹配,而是把每个正则表达式当场编译成 IBM 7090 的机器码,让匹配以原生速度运行——这可以算是即时编译(JIT)思想的极早期实践。1967 年他为此申请专利,1971 年获准(美国专利 3,568,156,"文本匹配算法"),是史上最早的软件专利之一。
到贝尔实验室后,汤普森为 PDP-7 写出了 Unix 的标准编辑器 ed,正则表达式随之进入 Unix。没过多久,他把 ed 中"全局搜索并打印匹配行"的命令 g/re/p 抽出来做成了独立工具——grep 的名字就来自这条命令。
汤普森 1968 年发表在 CACM 上的匹配算法论文还回答了"为什么可以这么快":他的构造把正则表达式转换为状态机后同时跟踪所有可能的当前状态,而不是沿单一路径试探、失败后回溯。这保证了匹配时间与文本长度成线性关系,不会像回溯法那样在某些模式上指数级爆炸。半个世纪后,这一课仍在被反复重学——不少现代正则库因为选择回溯实现,重新引入了同样的陷阱。
Belle:一台专门下棋的机器
1970 年代末,汤普森与贝尔实验室同事乔·康登(Joe Condon)合作建造了 Belle——一台不靠通用计算机、而靠专用硬件下棋的机器。康登负责硬件,汤普森负责软件。设计思路朴素而直接:棋力主要来自搜索深度,搜索深度来自速度,于是他们把走法生成和局面评估做成专用电路,让穷举搜索跑得比通用机器上的任何程序都快。
Belle 于 1978 年首夺 ACM 北美计算机国际象棋锦标赛冠军,1980 年在奥地利林茨赢得第三届世界计算机国际象棋锦标赛,1983 年 10 月成为第一台获得美国棋联(USCF)大师等级分(2203)的机器。Belle 验证了"暴力搜索+专用硬件"路线的威力,这条路线一直通向 1997 年击败卡斯帕罗夫的深蓝。
UTF-8:全球互联网的字符编码基础
1992 年 9 月的一个晚上,汤普森与罗布·派克(Rob Pike)在新泽西州一家餐厅的餐垫纸(placemat)背面设计了 UTF-8 字符编码方案。
在此之前,字符编码是互联网的一大混乱来源:ASCII 只能表示英语字符,各国有各自不兼容的编码方案(中文有 GB2312、Big5,日文有 Shift-JIS 等),网页乱码是家常便饭。
UTF-8 的设计天才在于它的向后兼容性:ASCII 字符(0–127)在 UTF-8 中仍然用一个字节表示,完全与原有 ASCII 兼容;非 ASCII 字符则用 2–4 个字节编码,且有自我同步的边界标记,可以在任意字节位置开始解码,不会产生歧义。
今天,UTF-8 是互联网上占主导地位的字符编码,覆盖约 98% 的网页,支持世界上几乎所有书写系统的字符。这张餐垫纸背面的涂写,是今天你能在网页上看到中文、阿拉伯文和表情符号的技术基础。
Go 语言:第二个操作系统级语言
汤普森晚年加入 Google。2007 年 9 月,他与罗布·派克、罗伯特·格里斯默(Robert Griesemer)开始设计 Go 编程语言,2009 年 11 月对外开源。
Go 的出发点是一个具体的工程痛点:在 Google 的代码规模下,C++ 的构建慢到以小时计,头文件依赖层层缠绕,而并发代码既必要又极易写错。三人的回答与 Unix 一脉相承——把语言做小:砍掉的特性远多于增加的,编译必须快到不打断思路。Go 用垃圾回收卸下内存管理负担,用 goroutine 加 channel 提供轻量级并发;这套并发模型的思想谱系可以追溯到托尼·霍尔(Tony Hoare)1978 年的 CSP(通信顺序进程)——并发单元之间不共享内存,而是靠传递消息协调,从机制上消解了一大类数据竞争。
Go 今天被广泛用于云基础设施(Docker、Kubernetes 都用 Go 写成),是云计算时代的系统语言担当。
图灵奖与"反英雄"气质
1983 年,汤普森与里奇共同获得图灵奖。他的获奖演讲《信任信任》("Reflections on Trusting Trust")是计算机安全史上最著名的文章之一。
演讲中,他描述了一种被称为"编译器后门"的攻击:如果你控制了编译器(把源代码翻译成可执行文件的程序),你就可以在编译器中植入代码,使得它在编译某些程序时自动插入后门——即使目标程序的源代码完全干净,你也可以用肉眼看不到的方式攻击它。更邪恶的是,你可以让编译器在编译它自己时插入这段代码,于是即使有人重新编译了编译器的源码,后门仍然存在。
这个论证展示了计算机信任链的根本脆弱性:你信任的不是代码本身,而是整个软件供应链。这是今天"供应链攻击"安全问题的理论预言,比现实中的 SolarWinds 攻击早了近四十年。
跨域连接
- 形态学:把功能切成小工具、用统一的文本流拼装,表达力来自组合而非单个工具的能力。这与少量语素按规则组合出无穷词形是同一策略,前提也相同:必须有一个稳定、可预测的接合面。推论可以立刻验证——一旦接口不统一(各家二进制格式、结构化输出),组合的收益当场消失。
- Unicode 与数字书写:那套编码把"从任意字节位置都能找回字符边界"写进了编码本身——首字节与续字节的高位模式互不重叠,流被截断后仍能重新同步。同时它对旧有的单字节文本逐字节兼容,于是既有工具无需改动就能转发。可用性来自这两条性质,而不是来自表达了多少字符。
- 教育与文凭:源码可读,使操作系统成了能被逐行讲授的教材,"读过内核"由此变成一代研究者共享的底层经验,比任何标准文件都更有效地统一了后来的设计直觉。这条链路断裂时后果也立刻显现:许可收紧后教学必须转向替代品,而一个学生自己写内核的故事正是在那条缝隙里开始的。
- 劳动与工作组织:他后来参与设计的语言,许多取舍——编译要快、语言要小、格式统一、长期拒绝复杂泛型——优化的是"大量中等水平工程师协作"这个目标函数,而不是单个程序的表达力。所以评价分歧其实来自评价者所处的组织规模:小团队嫌它啰嗦,大组织嫌别的语言失控。
- 软件供应链安全:《信任信任》的要害是审读源码不足以保证产物安全,因为编译器可以在编译它自己时重新植入后门——信任的根不在你能读的东西里。工程上的出路因此不是读得更仔细,而是让两条互不依赖的工具链产出可比对的结果,可重现构建正是在打断这条递归。
参考文献
- Thompson, K. Reflections on Trusting Trust. Communications of the ACM 27(8) (1984): 761–763. (图灵奖演讲,必读)
- Thompson, K. Regular Expression Search Algorithm. Communications of the ACM 11(6) (1968): 419–422. (非回溯正则匹配的原始论文)
- Condon, J. H. & Thompson, K. Belle Chess Hardware. In: Advances in Computer Chess 3, Pergamon Press (1982). (Belle 专用硬件的设计说明)
- Pike, R. & Thompson, K. Hello World or Καλημέρα κόσμε or こんにちは 世界. Proc. USENIX Winter Technical Conf. (1993). (UTF-8 的原始描述文档)
延伸阅读
- Kernighan, B. W. Unix: A History and a Memoir. Self-published (2019). (亲历者的历史叙述)