Skip to content

第三章 Unix 哲学同其他哲学的比较

操作系统的设计在明显和微妙两方面造就了该系统下软件开发的风格。

3.1 操作系统的风格元素

开始讨论特定的操作系统之前,我们需要一个组织框架,来了解操作系统的设计是如何对编程风格产生健康或病态的影响。

总的来说,与不同操作系统相关的设计和编程风格可以追溯出三个源头:

  • 操作系统设计者的意图
  • 成本和编程环境的限制对设计的均衡影响
  • 文化随机飘逸,传统无非就是陷入为主

3.1.1 什么是操作系统的统一性理念

Unix 有几个统一性的理念或象征,并塑造了它的 API 及由此形成的开发风格。其中最重要的一点应当是“一切皆问文件”模型及在此基础上建立的管道概念。

3.1.2 多任务能力

各种操作系统最基本的不同之处就是操作系统支持多进程并发的能力。最低端的操作系统(如 DOS),基本上就是一个顺序的程序加载器,根本不具备多任务能力。这种操作系统在通用计算机上已经毫无竞争力。

再往上一个层次,操作系统可具有协作式多任务能力。这种系统能够支持多个进程,但是一个进程运行前必须等待一个进程主动放弃占用处理器。这种系统也过时了。

Unix 系统拥有抢占式多任务能力。在 Unix 中,时间片由调度程序来分配,这个调度程序定期中断或抢断正在运行的进程而把控制权交给下一个进程。几乎所有的现代操作系统都支持抢占式调度。

3.1.3 协作进程

在 Unix 中,低成本的进程生成和简便的进程间通讯使得众多小工具、管道和过滤器组成一个均衡系统成为可能。

Doug Mcllroy

管道虽然在技术上容易实现,但影响却很大。进程是自主运算单元的统一性记号,而进程控制是可编程的——如果没有这些概念,那么管道技术就不可能这么简单。

在这里,我们需要指出代价高昂的进程生成和 IPC 会带来什么后果。如果操作系统的进程生成代价高昂,且进程控制非常困难、不灵活,后果通常是:

  • 编写怪物般巨大的单个程序成为更自然的编程方式。
  • 很多策略必须在这些庞大程序中表述。这会助长使用 C++ 和诡谲的内部代码层级,而不是 C 和相对平坦的内部层级。
  • 当进程间不得不进行通讯时,要只能采用笨拙的、低效的、不安全的机制,要么就得依赖太多彼此的实现细节,要么彼此需了解对方的太多实现细节。
  • 广泛使用多线程来完成某些任务,而这些任务 Unix 只需用互通的多进程就能处理。
  • 必须学习和使用异步 I/O。

这些就是操作系统环境的局限性所导致的常见风格缺陷的实例。

管道和所有其他经典 Unix IPC 方法又一个精微的性质,就是要求把程序间的通讯简化到某一程度而促使功能分离。相反地,如果没有与管道等效的机制,则程序必须在完全相互了解对方内部细节的基础上设计程序,才能实现彼此间的合作。

一个操作系统,如果没有灵活的 IPC 和使用 IPC 的强大传统,程序间就得通过共享结构复杂的数据实现通讯。由于一旦有新的程序加入通讯圈,圈子里所有程序的通讯问题都必须重新解决,所以解决方案的复杂度与协作数量的平方成正比。更糟糕的是,其中任何一个程序的数据结构发生变化,都说不定回给其他程序带来什么隐蔽的 bug。

Doug Mcllroy

Word、Excel、PowerPoint 和其他微软程序对彼此的内部具有“密切”的了解。在 Unix 中,一组程序设计时不仅要尽量考虑相互协作,而且要考虑和未知程序的协作

3.1.4 内部边界

Unix 的准绳是:程序员最清楚一切。当你对自己的数据进行危险操作时,Unix 并不阻止你,也不会让你确认。另一方面,Unix 却小心避免你踩在别人的数据上。

Unix 至少设立了三层内部边界来防范恶意用户或有缺陷的程序。一层是内存管理:Unix 用硬件自身的内存管理单元来保证各自的进程不会侵入到其他进程的内存地址空间。第二层是为多用户设置的真正权限组——普通用户的进程未经允许,就不能更改或者读取其他用户的文件。第三层是把涉及关键安全性的功能限制在尽可能小的可信代码块上。

3.1.5 文件属性和记录结构

Unix 文件既没有记录结构也没有文件属性。在一些操作系统中,文件具有相关的记录结构;操作系统通过固定长度的记录,了解文件,或者文本终止符是不是作为单个逻辑字符读取。

在另一些操作系统中,文件和目录可以具备相关的名字/属性对,采用编外数据将文档文件同能够解读它的应用程序关联起来。而 Unix 处理这种联系的典型方法是让应用程序识别“特征数”或是文件内的其他类型数据。

3.1.6 二进制文件格式

如果你的操作系统使用二进制文件格式存放关键数据,应用程序采用可读文本格式的传统就很可能无法形成。这种做法会带来一下结果。

  • 即使支持命令行接口、脚本和管道,也几乎无法形成过滤器
  • 数据文件只有通过专用工具才能访问。开发者的思维会以工具而非数据为中心。这样不同版本的文件格式很难兼容。

3.1.7 首选用户界面风格

第一款 Macintosh 已经发布很多年了,不用说人们也会觉得操作系统的 GUI 没做好的话会是一个大问题。Unix 的教训则相反:CLI 没做好是一个不太明显但同样严重的缺陷。

如果操作系统的 CLI 功能很弱或根本不存在,其后果会是:

  • 程序设计不会考虑以未预料到的方式相互协作——因为无法这样设计。输出不能用作输入。
  • 远程系统管理更难以实现,更难以使用。
  • 即使简单的非交互程序也将招致 GUI 开销或复杂的脚本接口
  • 服务器、守护程序和后台进程几乎无法写出,至少很难以优雅的方式写出。

3.1.8 目标受众

不同的操作系统设计是为了适应不同的目标受众。有的为后台工作设计,有的则设计成桌面系统。有的为技术用户设计,有的则为最终用户设计。有的能在实时控制应用中单机工作,有的则为分时系统和普遍联网的环境设计。

3.1.9 开发的门槛

区分操作系统的另一个重要尺度是纯用户转变为开发者的门槛高度。这又有两个重要的成本动因。一个是开发工具的金钱成本,另一个是成为一个熟练开发者的时间成本。

昂贵的开发工具和复杂晦涩的 API 造就了小群的精英编程文化。在这种文化中,编程是大型而严肃的活动。

廉价工具和简单接口支持的是轻松编程、玩家文化和开拓探索。

轻松编程往往会产生许多小程序和一个自我增强、不断拓展的知识社区。

Unix 开创了轻松编程的先河。Unix 的众多首创之一就是将编译器和众多脚本工具放在默认安装中,可供所有用户使用,支持了一种跨域众多机器的玩家开发文化。很多在 Unix 下写代码的人并不认为自己在写代码——他们认为是在为普通的任务编写自动化脚本,或在定制环境。

3.2 操作系统的比较

3.2.1 VMS

VMS 是一个专有操作系统,最初由数字设备公司为 VAX 小型机开发。VMS 于 1978 年面世,是二十世纪八十年代和九十年代早期一个非常重要的产业化操作系统产品,尽管今天已经没有多少人用它搞开发了。在这里提出 VMS,是为了对比 Unix 和来自小型机时代的其他 CLI 操作系统。

VMS 具有完全抢占式多任务处理能力,但是进程生成的开销极为昂贵。VMS 文件系统具有复杂的记录类型概念。这些特性造成了此前描述的后果。

VMS 的特点是具有长长的、可读的、类 COBOL 的系统命令和命令选项。它具有非常全面的在线帮助。事实上 VMS 命令行界面及其帮助系统就代表了 VMS 的组织结构。尽管在该系统上已经具有翻新版的 X Window,但冗长的 CLI 仍然对编程设计的风格产生了重要影响。主要可以理解为:

  • 命令行功能的使用频率——要打的字越多,愿意用的人越少。
  • 程序的大小——人们希望少打字,因此想少用几个软件,于是将更多功能捆绑到大型程序中。
  • 程序可接受选项的数量和类型——必须遵守帮助系统规定的语法限制。
  • 帮助系统的易用性——很完备,但缺少辅助的搜索和查找工具,并且索引做的很差。这样不容易获取大量的知识,鼓励了专业化而阻碍了轻松编程。

VMS 工具最初很贵,界面也很复杂。大卷大卷的 VMS 文档只有纸张形式,因此想要查找任何东西都很费时费力,这往往会阻碍探索性编程,减低人们对工具包的学习兴趣。

VMS 在它的时代是成功的。VMS 的目标受众基本是技术用户和大量应用软件的商业领域,这也意味着用户对其复杂度尚能容忍。

3.2.2 MacOS

Macintosh 操作系统是 1980 年代初由 Apple 公司设计的,灵感来自此前 Xerox 公司 Palos Alto 研究中心在 GUI 方面的开拓性工作。

MacOS 有一个不同于 Unix 的坚定统一性理念:Mac 界面方针。这些方针非常详细地说明了应用程序 GUI 的表现形式和行为模式。这些原则的一致性在很多重要方面影响了 Mac 用户的文化。

虽然 MacOS 的应用非常注重 GUI,然而,MacOS 的应用程序并非总是庞然大物。系统的 GUI 支持代码部分在硬件自带的 ROM 实现,部分在共享库中是新,通过事件接口同 MacOS 进行通信,这个接口从诞生起就一直相当稳定。这样,这种操作系统的设计提倡的是把应用引擎和 GUI 接口相对清晰的分离开来。

Macintosh 的目标是作为服务非技术终端用户的客户端操作系统,这就意味着用户对界面复杂度的容忍度很低。Macintosh 文化下的开发者于是非常擅长设计简洁的界面。Maac 很早也形成了一种浓厚的玩家文化,开发小工具、共享软件和用户支持软件的传统一直非常盛行。

3.2.3 OS/2

3.2.4 Windows NT

Winddows NT 是微软为高端个人用户和服务器设计的操作系统;发行的版本实际上有好几个,我们为了讨论方便把它们视为一个系统。自从 2000 年公布的 Windows ME 终结后,目前所有的 Windows 操作系统都以 Windows NT 为基础;Windows 2000 是 NT5,Windows XP 是 NT5.1。NT 起源自 VMS,很多重要特性与 VMS 相同。

NT 是逐步堆积而成的,缺乏对应于 Unix “一切皆文件”或 MacOS 桌面的统一性理念。由于它的核心技术没有扎根于一小群稳固的中枢概念中,因此没过几年就会过时,每一代技术都随着旧方式被宣告过时而不再有良好支持,开发者必须从头学起。

Unix 的系统配置和用户配置数据分散存在众多的 dotfiles 和系统数据文件中,而 NT 则集中存放在注册表中。以下后果贯穿其设计中:

  • 注册表使得整个系统完全不具备正交性。应用程序的单点故障就会损坏注册表,经常使得整个操作系统无法使用。
  • 注册表蠕变现象:随着注册表的膨胀,越来越大的存取开销拖慢了所有程序的运行。

3.2.5 BeOS

3.2.6 MVS

3.2.7 VM/CMS

3.2.8 Linux

Linux 由 Linus Torvalds 于 1991 年发明,是 1990 年后出现的新学派开源 Unix 阵营的领头羊,代表了整个阵营的设计方向。Linux 的技术趋势可视为整个阵营的典型。

Linux 并不含任何来自原始 Unix 源码树的代码,但却是一个依照 Unix 标准设计、行为像 Unix 的操作系统。

Linux 社区的许多开发者和积极分子都有夺取足量桌面用户市场份额的雄心壮志。这一点影响了 Linux 黑客设计软件的方式。

最明显的变化就是首选界面风格的转变。 Linux 的用户和开发者不断自我调整来消弭非技术用户对 CLI 的恐惧。他们比旧学派 Unix 更看重 GUI 及其工具的开发。贴近终端用户的愿望使得 Linux 开发者比专有 Unix 更注重系统安装的平稳性和软件发布问题。由此产生的结果就是 Linux 的二进制包系统远比专有 Unix 的类似系统复杂。

3.3 种什么籽,得什么果

Unix 的竞争对手最明显得通病就是不可移植性。大部分 1980 年前的 Unix 竞争者都被栓到某个硬件平台上,随着这个硬件消亡而消亡。