If you have any trouble sounding condescending, find a Unix user to show you how it's done.
如果你不知道怎样表现得高人一等,找个Unix用户,让他做给你看。
Dilbert newsletter 3.0, 1994
呆伯通讯 3.0,1994 年
—Scott Adams
操作系统的设计,在明显和微妙两方面,造就了该系统下软件开发的风格。本书大部分内容描绘了此两者之间的联系:Unix操作系统设计,以及由此发展出的编程设计哲学。为了便于对照,我们不妨把经典的Unix方式和其它主要操作系统的设计和编程习俗作一番比较。
开始讨论特定的操作系统之前,我们需要一个组织框架,来了解操作系统的设计是如何对编程风格产生或健康或病态的影响。
总的来说,与不同操作系统相关的设计和编程风格可以追溯出三个源头:(a)操作系统设计者的意图;(b)成本和编程环境的限制对设计的均衡影响;(c)文化随机漂移,传统无非就是先入为主。
即使我们承认每个操作系统社区中都存在文化随机漂移现象,那么去探究一下设计者的意图和成本及环境造成的局限也能揭示一些有趣的规律,帮助我们通过比对来更好地理解Unix风格。我们可以通过分析操作系统最重要的不同之处把这些规律明确化。
Unix有几个统一性的理念或象征,并塑造了它的API及由此形成的开发风格。其中最重要的一点应当是“一切皆文件”模型及在此基础上建立的管道概念 。总的来说,任何特定操作系统的开发风格均受到系统设计者灌注其中的统一性理念的强烈影响——由系统工具和API塑造的模型将反渗到应用编程中。
相应地,将Unix和其他操作系统作比较时,最基本的问题是:这个操作系统存在对其开发有具有决定作用的统一性理念吗?如果有,它和Unix的统一性理念有何不同?
彻头彻尾的反Unix系统,就是没有任何统一性理念,胡乱堆砌起的一些唬人特性而已。
各种操作系统最基本的不同之处之一就是操作系统支持多进程并发的能力。最低端的操作系统(如DOS或CP/M),基本上就是一个顺序的程序加载器,根本不具备多任务能力。这种操作系统在通用计算机上已经毫无竞争力。
再往上一个层次,操作系统可具有 协作式多任务 ( cooperative multitasking ) 能力。这种系统能够支持多个进程,但是一个进程运行前必须等待前一个进程主动放弃占用处理器(这样一来,简单的编程错误就很容易将机器挂起)。这种操作系统风格是对一种硬件的暂时性适应,这种硬件虽然功能强大到支持并行操作,但要么缺乏周期性时钟中断 ,要么缺乏内存管理单元,或者两者都缺。这种系统也过时了,不再具有竞争力了。
Unix系统拥有 抢先式多任务 ( preemptive multitasking ) 能力。在Unix中,时间片由调度程序来分配,这个调度程序定期中断或抢断正在运行的进程而把控制权交给下一个进程。几乎所有的现代操作系统都支持抢占式调度。
注意,“多任务”跟“多用户”不是一回事。一个操作系统可以进行多任务处理而只支持单用户,在这种情况下,计算机支持的是单个控制台和多个后台进程。真正的多用户支持需要多个用户权限域,我们将在随后讨论内部边界时进一步讨论这个特性。
彻头彻尾的反Unix系统,就是绝无多任务处理能力——或者通过对进程管理增设诸多的规定、限制和特殊情况来削弱多任务能力——的一个废物。
在Unix中,低价的进程生成和简便的进程间通讯(IPC Inter-Process Communication)使众多小工具、管道和过滤器组成一个均衡系统成为可能。我们将在第 7 章探讨这个均衡体系。在这里,我们需要指出代价高昂的进程生成和IPC会带来什么后果。
管道虽然在技术上容易发现,但影响却很大。进程是自主运算单元的统一性记号、而进程控制是可编程的——如果没有这些概念,那么管道技术就不可能这么简单。和Multics一样,Unix的shell(外壳)只是另外一个进程;进程控制并非受JCL(作业控制语言)之赐。
—Doug McIlroy
如果操作系统的进程生成代价昂贵,且/或进程控制非常困难、不灵活,后果通常是:
● 编写怪物般巨大的单个程序成为更自然的编程方式。
● 很多策略必须在这些庞大程序中表述。这会助长使用C++和诡谲的内部代码层级,而不是C和相对平坦的内部层级。
● 当进程间不得不进行通讯时,要么只能采用笨拙、低效、不安全的机制(比如临时文件),要么就得依赖太多彼此的实现细节,要么彼此需了解对方的太多实现细节。
● 广泛使用多线程来完成某些任务,而这些任务Unix只需用互通的多进程就能处理。
● 必须学习和使用异步I/O。
这些就是操作系统环境的局限性所导致的常见风格缺陷(甚至应用程序编程中也一样)的实例。
管道和所有其他经典Unix IPC方法有一个精微的性质,就是要求把程序间的通讯简化到某一程度而促使功能分离。相反地,如果没有与管道等效的机制,则程序必须在完全相互了解对方内部细节的基础上设计程序,才能实现彼此间的合作。
一个操作系统,如果没有灵活的IPC和使用IPC的强大传统,程序间就得通过共享结构复杂的数据实现通讯。由于一旦有新的程序加入通讯圈,圈子里所有程序的通讯问题都必须重新解决,所以解决方案的复杂度与协作程序数量的平方成正比。更糟糕的是,其中任何一个程序的数据结构发生变化,都说不定会给其它程序带来什么隐蔽的bug。
Word、Excel、PowerPoint和其他微软程序对彼此的内部具有“密切”——有些人可能称之为“杂乱”——的了解。在Unix中,一组程序设计时不仅要尽量考虑相互协作,而且要考虑和未知程序的协作。
—Doug McIlroy
我们将在第 7 章再谈这个主题。
彻头彻尾的反Unix系统,就是让进程的生成代价高昂,让进程的控制困难而死板,让IPC可有可无,对它不予支持或支持很少。
Unix的准绳是:程序员最清楚一切。当你对自己的数据进行危险操作(例如执行rm-rf *.)时,Unix并不阻止你,也不会让你确认。另一方面,Unix却小心避免你踩在别人的数据上。事实上,Unix提倡设立多个帐户,每一个帐户具有专属、可能不同的权限,以保护用户不受行为不端程序的侵害 。系统程序通常都有自己的“伪用户(pseudo-user)帐号”,以访问专门的系统文件,而不需要无限制的(或者说超级用户的)访问权限。
Unix至少设立了三层内部边界来防范恶意用户或有缺陷的程序。一层是内存管理:Unix用硬件自身的内存管理单元(MMU)来保证各自的进程不会侵入到其它进程的内存地址空间。第二层是为多用户设置的真正权限组——普通用户(非root用户)的进程未经允许,就不能更改或者读取其他用户的文件。第三层是把涉及关键安全性的功能限制在尽可能小的可信代码块上。在Unix中,即使是shell(系统命令解释器)也不是什么特权程序。
操作系统内部边界的稳定不仅是一个设计的抽象问题,它对系统安全性有着重要的实际影响。
彻头彻尾的反Unix系统,就是抛弃或回避内存管理,这样失控的进程就可以任意摧毁、搅乱或破坏掉其它正在运行的程序;弱化甚至不设置权限组,这样用户就可以轻而易举地修改他人的文件和系统的关键数据(例如,掌控了Word程序的宏病毒可以格式化硬盘);依赖大量的代码,如整个shell和GUI,这样任何代码的bug或对代码的成功攻击都可以威胁到整个系统。
Unix文件既没有记录结构(record structure)也没有文件属性。在一些操作系统中,文件具有相关的记录结构:操作系统(或其服务程序库)通过固定长度的纪录,了解文件,或文本行终止符以及CR/LF(回车/换行)是不是该作为单个逻辑字符读取。
在另一些操作系统中,文件和目录可以具备相关的名字/属性对——(例如)采用编外数据(out-of-band data)将文档文件同能够解读它的应用程序关联起来。(Unix处理这种联系的典型方法是让应用程序识别“特征数”或是文件内的其它类型数据。)
操作系统级的记录结构通常只是一个优化手段,几乎只会使API和程序员的生活复杂化之外没别的用,还会助长不透明的面向记录的文件格式,使得文本编辑器之类的通用工具无法正确读取。
文件属性会很有用,但是(我们在第 20 章将发现)在面向字节流工具和管道的世界中,它可能引发一些棘手的语义问题。对文件属性的操作系统级支持会诱导程序员使用不透明的文件格式,让他们依靠文件属性将文件格式同对应的解读程序绑在一起。
彻头彻尾的反Unix系统,应用一套拙劣的记录结构,任何特定的工具能否像文件编写者希望的那样读懂文件,完全是靠运气。加入文件属性,并让系统依赖于这些文件属性,就无法通过查看文件内的数据来确定文件的语义。
如果你的操作系统使用二进制文件格式存放关键数据(如用户帐号记录),应用程序采用可读文本格式的传统就很可能无法形成。我们将在第 5 章详细解释为什么这是一个问题。现在只要注意,这种做法可能会带来以下后果就够了。
● 即使支持命令行接口、脚本和管道,也几乎无法形成过滤器。
● 数据文件只有通过专用工具才能访问。开发者的思维会以工具而非数据为中心。这样,不同版本的文件格式很难兼容。
彻头彻尾的反Unix系统,让所有文件格式都采用不透明的二进制格式,后者要用重量级的工具才能读取和编辑。
我们将在第 11 章详细讨论 命令行界面 (CLI)和 图形用户界面 (GUI)的差异所产生的影响。操作系统的设计者把哪一种选作一般表现模式,将影响设计的许多方面——从进程调度、内存管理直到应用程序使用的 应用程序编程接口 (API)。
第一款Macintosh已经发布很多年了,不用说人们也会觉得操作系统的GUI没做好是个问题。Unix的教训则相反:CLI没做好是一个不太明显但同样严重的缺陷。
如果操作系统的CLI功能很弱或根本不存在,其后果会是:
● 程序设计不会考虑以未预料到的方式相互协作——因为无法这样设计。输出不能用作输入。
● 远程系统管理更难于实现,更难以使用,更强调网络。
● 即便简单的非交互程序也将招致GUI开销或复杂的脚本接口。
● 服务器、守护程序和后台进程几乎无法写出,至少很难以优雅的方式写出。
彻头彻尾的反Unix系统,就是没有CLI,没有脚本编程能力——或者,存在CLI不能驱动的重要功能。
不同的操作系统设计是为了适应不同的目标受众。有的为后台工作设计,有的则设计成桌面系统。有的为技术用户而设计,有的则为最终用户设计。有的能在实时控制应用中单机工作,有的则为分时系统和普遍联网的环境设计。
一个重要的差异是客户端与服务器之分。“客户端”可以理解为:轻量,只支持单个用户,能够在小型机器上运行,随需开关机器,没有抢先式多任务处理,为低延迟作了优化,大量资源都用在花哨的用户界面上。“服务器”可以解释为:重量,能够连续运行,为吞吐量优化,完全抢占式多任务处理以处理多重会话。所有的操作系统最初都是服务器操作系统。客户端操作系统的概念仅在二十世纪七十年代后期随着价格不高、性能一般的PC硬件的出现才产生。客户端操作系统更关注用户的视觉体验,而不是 7*24小时的连续正常运行。
所有这些变数都对开发风格产生影响。其中最明显的就是目标用户能够容忍的界面复杂的级别,以及如何在可感知复杂度和成本、性能等其它变数之间权衡轻重。人们常说,Unix是程序员写给程序员的——这个目标用户群在界面复杂度的承受力方面是出了名的。
这与其说是一个目标不如说是一个结果。如果“用户”这个词带有“单纯得傻乎乎”的蔑视含义,我憎恨一个为“用户”设计的系统。
—Ken Thompson
彻头彻尾的反Unix系统,就是一个自认为比你自己更懂你在干什么的操作系统,然后雪上加霜的是,它还做错了。
区分操作系统的另一个重要尺度是纯用户转变为开发者的门坎高度。这里有两个重要的成本动因。一个是开发工具的金钱成本,另一个是成为一个熟练开发者的时间成本。有些开发文化还形成了一个社会性门坎,但这通常是背后的技术成本带来的结果,而不是根本原因。
昂贵的开发工具和复杂晦涩的API造就了小群的精英编程文化。在这种文化中,编程项目是大型而严肃的活动——为了证明所投资的软(人力)硬资本物有所值,这些工程必须如此。大型而严肃的工程常常产生大型、严肃的程序(而且,更常见的是,大型而昂贵的失败)。
廉价工具和简单接口支持的是轻松编程、玩家文化和开拓探索。编程项目可以很小(通常,正式的项目结构显然毫无必要),失败了也不是什么大灾难。这改变了人们开发代码的风格;尤其是,他们往往不会过分依赖已经失败的方法。
轻松编程往往会产生许多小程序和一个自我增强、不断扩展的知识社区。在廉价硬件的世界里,是否存在这样一个社区日益成为一个操作系统能否长寿的重要因素。
Unix开创了轻松编程的先河。Unix的众多首创之一就是将编译器和脚本工具放在默认安装中,可供所有用户使用,支持了一种跨越众多机器的玩家开发文化。在很多Unix下写代码的人并不认为自己在写代码——他们认为是在为普通任务的自动化编写脚本,或在定制环境。
彻头彻尾的反Unix系统,不可能进行轻松编程。
当我们将Unix和其他操作系统对比时,Unix设计决策的逻辑就更清楚了。这里,我们只是纵览各种设计,对各操作系统技术特性的具体讨论请参考OSData网站。
图 3.1 表明我们要纵览的各种分时系统之间的渊源关系。其它一些操作系统(灰色标记,不一定是分时系统)因脉络的关系也包括进来了。实线框里的系统仍旧存在。“出生”日期是指第一次发布的时间; “死亡”日期通常指厂商终结系统的时间。
实线箭头表示存在起源关系或很强的设计影响(例如,后开发的系统API有意通过逆向工程以匹配先前的系统);短画线表示重大的设计影响;点线表示微弱的设计影响。不是所有的起源关系被开发者认可;实际上,有些出于法律或企业策略的原因被官方否认,但在业界其实是公开的秘密。
“Unix”框包括所有的专有Unix,包括AT&T版本和早期的伯克利版本。“Linux”框包括所有的开源Unix(均在 1991 年开始)。这些开源Unix与早期的Unix有渊源关系,它建立在 1993 年诉讼协议后从AT&T专有控制下解放出来的代码的基础上。
VMS 是一个专有操作系统,最初由数字设备公司(Digital Equipment Corporation)为VAX小型机开发。VMS于 1978 年面世,是二十世纪八十年代和九十年代早期一个非常重要的产业化操作系统产品,无论在Compaq并购DEC,还是Hewlett-Packard并购Compaq之后,这个系统一直得到了维护。直到 2003 年中期,这个产品仍在销售和支持,尽管今天已经没有多少人用它搞新的开发了 。在这里提出VMS,是为了对比Unix和来自小型机时代的其它CLI操作系统。
图 3.1 分时系统历史示意图
VMS具有完全抢占式多任务处理能力,但是进程生成的开销极为昂贵。VMS文件系统有复杂的记录类型(但还不是记录属性)概念。这些特性造成了我们此前描述的后果,尤其(在VMS中)是程序庞大、个体臃肿的倾向。
VMS的特点是具有长长的、可读的、类COBOL的系统命令和命令选项。它具有非常全面的在线帮助(针对的不是API,而是可执行程序和命令行语法)。事实上,VMS命令行界面及其帮助系统就代表了VMS的组织结构。尽管在该系统上已经具有翻新版的X window,但冗长的CLI仍然对编程设计的风格产生了重要影响。主要可以理解为:
● 命令行功能的使用频率——要打的字越多,愿意用的人就越少。
● 程序的大小——人们希望少打字,因此想少用几个软件,于是将更多功能捆绑到大型程序中。
● 程序可接受选项的数量和类型——必须遵守帮助系统规定的语法限制。
● 帮助系统的易用性——很完备,但缺少辅助的搜索和查找工具,并且索引做得很差。这样不容易获取大量的知识,鼓励了专业化而阻碍了轻松编程。
VMS的内部边界系统有口皆碑。它为真正的多用户操作而设计,完全利用硬件MMU来保护进程互不干扰。系统命令解释器具有优先权,但在另一方面,关键功能封装则做得相当不错。VMS的安全漏洞一直都很罕见。
VMS工具最初很贵,界面也很复杂。大卷大卷的VMS程序员文档只有纸张形式,因此要查找任何东西都既费时又费钱。这往往会阻碍探索性编程,降低人们对大型工具包的学习兴趣。直到几乎被厂商抛弃之后,VMS才形成一种轻松编程和玩家文化,但这种文化并非很强。
和Unix一样,VMS早就有了客户端/服务器的划分。作为一个通用分时系统,VMS在它的时代是成功的。VMS的目标受众基本是技术用户和大量应用软件的商业领域,这也意味着用户对其复杂度尚能容忍。
Macintosh操作系统是 1980 年代初由Apple公司设计的,灵感来自此前Xerox公司Palo Alto 研究中心在GUI方面的开拓性工作。1984 年与Macintosh计算机一起面世。MacOS经历过两场重大的设计变革,第三场正在酝酿中。第一次是从一次只支持一个应用程序转变到能够多任务协作处理多个应用程序(MultiFinder);第二次是从 68000 处理器到PowerPC处理器的转变,既保留了 68K应用程序的二进制向后兼容,又为PowerPC应用程序引入了高级共享库管理系统,代替了原来的 68K基于陷阱指令的代码共享系统(trap instruction-based code sharing system);第三次是在MacOS X中把MacOS设计理念和来自Unix的架构融合起来。如果没有特别指出,此处讨论仅限OS-X之前的版本。
MacOS有一个不同于Unix的坚定统一性理念:Mac界面方针(the Mac Interface Guidelines)。这些方针非常详细地说明了应用程序GUI的表现形式和行为模式。这些原则的一致性在很多重要方面影响了Mac用户的文化。不遵循这些原则、简单移植DOS或Unix程序的产品立即遭到了Mac用户的拒绝,在市场上一败涂地,而这并不罕见。
这些方针的主旨是:东西永远呆在你摆的地方。文档、目录和其它东西在桌面上都有固定的、系统不会弄乱的位置,重启后桌面依然保持原样。
Macintosh的统一性理念非常强大,我们上面讨论过的其它设计方案要么受其影响,要么就无人问津。所有的程序都得有GUI,根本没有CLI。脚本的功能有倒是有,但绝对不像Unix中那样常用,很多Mac程序员根本就不去学习。MacOS界面至上的GUI做法(被组织到单个的主事件循环中)导致了其薄弱的非抢占式的程序调度能力。这个弱程序调度器以及所有的MultiFinder应用程序都在单个大地址空间运行,这意味着使用分离的进程甚或线程来代替轮询是不现实的。
然而,MacOS的应用程序并非总是庞然大物。系统的GUI支持代码,部分在硬件自带的ROM实现,部分在共享库中实现,通过事件接口同MacOS中的程序进行通信,这个接口从诞生起就一直相当稳定。这样,这种操作系统的设计提倡的是把应用引擎和GUI接口相对清晰地分离开来。
MacOS也强烈支持把应用程序的元数据(如菜单结构)从引擎中隔离。MacOS文件分“数据分支”(data fork)(Unix风格的字节包,包含文档或程序代码)和“资源分支”(resource fork)(一套用户定义的文件属性)。Mac应用程序通常是这样设计的,(比如)程序中使用的图像和声音存储在资源分支中,可以独立于应用程序码进行修改。
MacOS系统的内部边界系统很弱。因为基于只有单个用户这样的固定设想,所以没有用户权限组。多任务处理是协作式的,不是抢占式的。所有的MultiFinder应用都在同一个地址空间运行,所以任何应用程序的不良代码都能破坏操作系统低层内核以外的任何部分。针对MacOS机器的安全攻击程序很容易编写,这个系统一直没遭到大规模攻击只是因为没人有兴趣罢了。
Mac程序员和Unix程序员在设计上往往走截然相反的路,即,他们的设计是从界面向内进行,而不是从引擎向外进行(我们将在 20 章讨论这种方式的影响)。MacOS的一切设计共同促成了这种做法。
Macintosh的目标是作为是服务非技术终端用户的客户端操作系统,这就意味着用户对界面复杂度的容忍度很低。Macintosh文化下的开发者于是非常非常擅长设计简洁的界面。
假设你已经有Macintosh机器,那么晋级为开发者的代价一向不高。因此,尽管界面相当复杂,Mac很早也形成了一种浓厚的玩家文化。开发小工具、共享软件和用户支持软件的传统一直非常盛行。
经典的MacOS已经寿终正寝。MacOS大多数功能已被引入MacOS X,并同源自Berkeley传统的Unix架构结合在一起 。同时,像Linux这样的前沿Unix也开始从MacOS中借鉴一些理念,如文件属性(资源分支的泛化)。
OS/2 是作为IBM命名为“ADOS”(Advanced DOS)的开发项目诞生的,也是想成为DOS 4 的三个竞争者之一。那时,IBM和微软在正式合作,为PC机开发下一代操作系统。OS/2 1.0 版本首发于 1987 年,为 286 机开发,并不成功。针对 386 的 2.0 版本发布于 1992 年,但那时IBM和微软联盟已经破裂。微软走向一个不同的(而且更赚钱的)方向——Windows 3.0。OS/2 虽然吸引了一小部分忠诚的拥趸,但从来没有吸引到足够多的开发者和用户。直到 1996 年后IBM把它纳入Java计划前,OS/2 在桌面市场一直排在Macintosh之后,位居第三。最新版本是 1996 年发布的 4.0 版本。那些早期的版本在嵌入式系统中找到了出路,时至 2003 年年中,还在全球众多银行自动柜员机上运行。
和Unix一样,OS/2 使用抢先式多任务处理,不能在没有MMU的机器上运行(早期版本使用 286 的内存分段来模拟MMU)。跟Unix不同的是,OS/2 从来都不是一个多用户系统。虽然它的进程生成开销相对较低,但是IPC困难而脆弱。网络能力最初仅限于LAN协议,但后续版本也增加了TCP/IP协议栈。因为没有类似于Unix的服务守护程序,所以,OS/2 处理多功能网络的能力一直欠佳。
OS/2 既有CLI又有GUI。OS/2 流传下来的亮点大多围绕它的桌面Workplace Shell(WPS)。有一些技术从AmigaOS Workbench 的开发者处授权得到。AmigaOS Workbench是GUI桌面的先驱,直到 2003 年,在欧洲还拥有众多忠实的爱好者。这也是OS/2 的能力超过Unix(这一点尚有争论)的唯一设计领域。WPS(Workplace Shell)是一个干净、强大、面向对象的设计,具有易懂的行为特性和良好的可扩展性。几年后,OS/2 成为Linux GNOME工程的模型。
WPS的类层次设计是OS/2 的统一性理念之一。另一个统一性理念是多线程处理。OS/2 程序员大量使用线程,部分代替了对等进程间的IPC,协作程序工具包传统也因此没能形成。
OS/2 的内部边界达到了单用户操作系统的预期。运行的进程互不干扰,内核空间也和用户空间互不干扰,但是没有了基于每用户的特权组。这意味着文件系统无法防范恶意代码。另一个结果是没有类似于起始目录的东西,应用程序的数据往往散布在整个系统中。
缺乏多用户能力所产生的进一步后果就是在用户空间不存在权限区别。这样,开发者往往只信任内核代码。Unix中许多由用户态守护进程处理的系统任务在OS/2 中只好塞进内核或WPS,结果是两者都臃肿。
OS/2 有一种和二进制模式相对的文本模式(文本模式下CR/LF被读作单个的行结束符,在二进制模式下无此含义),但是没有其它的文件记录结构。OS/2 支持文件属性,效仿Macintosh风格,用文件属性来支持桌面持久性。系统数据库大都是二进制格式。
首选UI风格贯穿于WPS。从人体工程学角度来说,WPS的用户界面要强于Windows,虽然还没有达到Macintosh的标准(OS/2 最活跃的时段在经典MacOS的历史上处于早期)。与Linux和Windows一样,OS/2 的用户界面围绕多个独立的窗口任务组,而不是让运行的应用程序占据整个桌面。
OS/2 的目标对象是商业和非技术的最终用户,意味着对界面复杂度的容忍度较低。OS/2 既可用作客户端操作系统,也可用作文件和打印服务器。
在二十世纪九十年代初期,OS/2 社区的开发者开始转向受Unix启发、模仿POSIX接口的EMX环境。到二十世纪九十年代后期,已经有很多Unix软件被移植到OS/2上。
任何人都可以下载EMX,包括GNU编译器集合以及其它开源开发工具。IBM不时在OS/2 开发包中发布系统文档,并被转载到了许多BBS和FTP站点上。正因为如此,到 1995 年,用户开发的OS/2 软件的“Hobbes”FTP档案已经超过了 1GB。一个崇尚小巧工具、探索编程和共享软件的强大传统形成了,即使OS/2 自身已经被丢进了历史的垃圾箱,这个传统仍然还会长期拥有一批忠诚的追随者。
在Windows 95 发布以后,OS/2 社区在微软的围剿和IBM的支援下,对Java的兴趣与日俱增。自Netscape在 1998 年初公开源码后,他们的方向又(陡然)转到了Linux上。
一个多任务处理、但单用户的操作系统到底能走多远?OS/2 是一个相当有趣的案例。从其得出的大部分结论都可以很好地运用到其它同类型操作系统中,尤其是AmigaOS 和GEM 。直到 2003 年,大量的OS/2 材料还可从网上获得,包括一些闪光的历史。
Windows NT(New Technology)是微软为高端个人用户和服务器设计的操作系统;发行的版本实际上有好几个,我们为了讨论方便把它们视为一个系统。自从 2000 年公布的Windows ME终结后,目前所有的Window操作系统都以Window NT为基础;Windows 2000 是NT 5,Windows XP(本书写作时是 2003 年)是NT 5.1。NT起源自VMS,很多重要特性与VMS相同。
NT是逐步堆积而成的,缺乏对应于Unix“一切皆文件”或MacOS桌面的统一性理念。由于它的核心技术没有扎根于一小群稳固的中枢观念中, 因此每过几年就会过时。每一代技术——DOS(1981),Windows 3.1(1992),Windows 95(1995),Windows NT 4(1996),Windows 2000(2000),Windows XP(2002)和 Windows Server 2003(2003)——随着旧方式被宣告过时而不再有良好支持,开发者必须以不同的方式从头学起。
下面是其它一些后果:
● GUI功能与继承自DOS和VMS的残留命令行界面不能稳定共存。
● 套接字编程没有类似Unix那种“一切皆是文件句柄”的统一数据对象,因此在Unix中很简单的多道程序设计和网络应用到NT下则要牵涉更多基础性概念。
NT的一些文件系统类型也有文件属性,但仅限用于为实现某些文件系统的访问控制列表,因此对开发风格不会产生太大影响。NT也有文本和二进制这两种记录类型区别,时不时地讨人嫌(NT和OS/2 都从DOS那里继承了这个不良特性)。
尽管支持抢先式多任务处理,但进程生成却很昂贵——虽然比不上VMS,但是(平均生成一个进程需要 0.1 秒左右)要比现在的Unix高出一个数量级。脚本功能薄弱,操作系统广泛使用二进制文件格式。除了此前我们总结过的,还有这些后果:
● 大多数程序都不能用脚本调用。程序间依赖复杂脆弱的远程过程调用(RPC)来通信,这是滋生bug的温床。
● 根本就不存在通用工具。没有专用软件就不可能读取或编辑文档和数据库。
● 随着时间的推移,CLI越来越被忽略了,原因是环境稀缺。薄弱CLI引起的问题不仅没有得到改善,反而越来越糟糕。(Windows Server 2003 试图稍稍扭转这种趋势。)
Unix的系统配置和用户配置数据分散存放在众多的dotfiles(名字以“。”开头的文件)和系统数据文件中,而NT则集中存放在注册表中。以下后果贯穿于设计中:
● 注册表使得整个系统完全不具备正交性。应用程序的单点故障就会损毁注册表,经常使得整个操作系统无法使用、必须重装。
● 注册表蠕变 (registry creep) 现象:随着注册表的膨胀,越来越大的存取开销拖慢了所有程序的运行。
互联网上的NT系统因易受各种蠕虫、病毒、损毁程序以及破解(crack)的攻击而臭名昭著。原因很多,但有一些是根本性的,最根本的就是:NT的内部边界漏洞太多。
NT有访问控制列表,可用于实现用户权限组管理,但许多遗留代码对此视而不见,而操作系统为了不破坏向后兼容性又允许这种现象的存在。在各个GUI客户端之间的消息通讯机制也没有安全控制 ,如果加上的话,也会破坏向后兼容性。
虽然NT将要使用MMU,出于性能方面的考虑,NT 3.5 后的版本将系统GUI和优先内核一起塞进了同一个地址空间。为了获得和Unix相近的速度,最新版本的NT甚至将Web服务器也塞进了内核空间。
由于这些内部边界漏洞产生的协合效应,要在NT上达到真正的安全实际上是不可能的 。如果入侵者随便作为什么用户把一段代码运行起来(例如,通过Outlook email宏功能),这段代码就可以通过窗口系统向其它任何运行的应用程序发送虚假信息。只要利用缓存溢出或GUI及Web服务器的缺口就可以控制整个系统。
因为Windows没有处理好程序库的版本控制问题,所以长期备受被称为“DLL地狱(DLL hell)”配置问题的折磨,在这个问题中,安装新程序可以任意升级(或降级)现有程序运行依赖的库文件。专用的应用程序库和厂商提供的系统库都存在这个问题:应用程序和特定版本的系统库一起发布非常普遍,一旦没有特定的系统库,应用程序就会无声无息地垮掉。
从好的一面来看,NT提供了足够的特性来支持Cygwin。Cygwin是一个在实用工具和API两个层次上实现Unix的兼容层,而且只有极少的特性损失 。Cygwin允许C程序既可以使用Unix API又可以使用原生API,许多为形势所迫不得不使用Windows的Unix黑客在Windows系统上安装的第一个程序就是Cygwin。
NT操作系统的目标用户主要是非技术型最终用户,意味着对界面复杂度的容忍度非常低。NT既可作客户端又可作服务器。
在其历史早期,微软依靠第三方开发商提供应用软件。起初,微软还公布Windows API的完整文档,并保持其开发工具的低价格。但是,随着时间的推移、竞争者的相继倒下,微软转而青睐内部开发的战略,开始向外界隐藏API,开发工具也越来越昂贵。早在Windows 95 时期,微软就要求将保密协议作为购买专业级开发工具的一个条件。
围绕DOS和Windows早期版本形成的玩家文化和轻松开发文化已经足够壮大,即使在微软日益加强的排挤(包括为了把业余开发者非法化而设立的各种认证计划)下也足以自我维系。共享软件从未消亡,而在 2000 年后,迫于开源操作系统和Java的市场压力,微软的策略也略有转变。但是,随着时间的推移,供“专业”编程使用的Windows接口越来越复杂,将轻松(或严肃!)编程的门槛越抬越高。
这段历史的后果就是业余NT和职业NT开发者的设计风格存在尖锐的分歧——两个群体之间几乎不通气。尽管小型工具和共享软件的玩家文化非常活跃,但职业NT项目却往往产出庞然大物,甚至比那些VMS一样的“精英”操作系统还要臃肿。
Windows下的Unix风格的shell功能、命令集和API函数库来自第三方,包括UWIN、Interix和开源Cygwin。
Be公司作为一家硬件厂商成立于 1989 年,基于PowerPC芯片开发了颇具开拓精神的多处理机器。BeOS操作系统是Be公司为给硬件增值而发明的一种新型、内置网络功能的操作系统模型,吸收了Unix和MacOS两个家族的经验教训,但又不和任何一个雷同。他们的努力造就了一个雅致、简洁、令人激动的设计,在其定位的多媒体平台这个角色上表现卓越。
BeOS的统一性理念是“深入地线程化”、多媒体流和数据库形式的文件系统。BeOS的设计目标是尽可能减少内核延迟,从而能非常适合实时处理大量数据,如音频和视频流。既然支持线程本地存储从而不需共享所有地址空间,BeOS的“线程”实际上就是Unix术语中的轻量级进程。IPC通过共享内存实现,快速而高效。
BeOS采用的是Unix模型,在字节级以上没有文件结构。BeOS和MacOS一样支持和使用文件属性。事实上,BeOS文件系统就是一个数据库,可以按任意属性索引。
BeOS借鉴Unix的设计是巧妙的内部边界设计。BeOS充分应用了MMU,而且有效地使各个运行进程互不干涉。虽然BeOS是个单用户操作系统(不用登录),但在文件系统和操作系统内部的其它地方都支持类似Unix的权限组。这些措施用于保护系统的关键文件免受不信任代码的侵袭:用Unix的术语来讲,就是用户在启动时作为匿名用户登录,另一个“用户”是root。如果需要完整的多用户操作,其实对系统上层产生的变化也会很小,实际上确实存在一个BeLogin实用程序。
BeOS倾向使用二进制文件格式和文件系统自带的数据库,而不使用类Unix的文本格式。
BeOS的首选UI风格是GUI,在界面设计上大量借鉴了MacOS的经验,但是完全支持CLI和脚本功能。BeOS的命令行shell是移植自Unix最主要的开源shell— bash (1),通过POSIX兼容库运行。移植Unix CLI软件在设计上相当容易。Unix模式的整套脚本、过滤器和守护进程的基础设施都到位了。
BeOS的目标定位是作为一个专门针对近实时(near-real-time)多媒体处理(尤其是音频和视频操控)的客户端操作系统。BeOS的目标受众包括技术和商业用户,这也意味着用户对界面复杂度的容忍度属中等。
BeOS的开发门槛很低:尽管操作系统是专有的,但是开发工具并不贵,而且很容易获得整套文档。BeOS项目起初部分为了通过RISC技术把Intel硬件拉下马,在互联网大爆炸后,继续往一个纯软件方向努力。在 1990 年代初Linux形成时期,BeOS的战略家就已经一直关注着、而且也充分意识到一个庞大的轻松开发者团体的价值。事实上,他们成功地吸引了一批非常忠诚的追随者;到 2003 年,至少有五个以上的不同工程正在努力试图用开源复兴BeOS。
不幸的是,BeOS的经营战略却不像其技术设计那样精明。起初,BeOS软件捆绑在专用硬件上,市场推广时对目标应用的说明也含混不清。后来(1998 年),BeOS被移植到通用PC机上,更紧密关注多媒体应用,但是从未吸引到足够数量的应用和用户群。最后,到 2001 年,BeOS死于微软的反竞争运动(2003 年仍在进行诉讼)和各种已具备多媒体功能的Linux的联合打击之下。
MVS(多重虚拟存储)是IBM大型计算机的旗舰操作系统,起源可以追溯到OS/360。OS/360 诞生于 1960 年代中期,是IBM当时很新型的System/360 计算机系统上向客户推荐的操作系统。今天IBM大型机操作系统的核心还保留着OS/360 的后裔代码。虽然整个代码几乎都已经重写了,但是基本设计大多原样未动;向后兼容性被虔诚地保留了下来。这种兼容性甚至达到这种地步:即使历经三代结构升级,为OS/360 编制的应用程序还能不加修改就在装有MVS的 64 位z/系列大型机上运行。
在上述讨论过的所有操作系统中,MVS是唯一可视为比Unix还要悠久的操作系统(不确定性在于随着时间的推移,MVS究竟发展到了什么地步)。这个操作系统也是受Unix概念和技术影响最小的操作系统,因而代表了跟Unix反差最强烈的一种设计。MVS的统一性理念是:一切皆批处理。系统的设计目标是尽可能最有效利用机器批处理巨大规模的数据,尽量减少与人类用户的交互。
原生的MVS终端(3270 系列)只能以块模式运行。用户通过屏幕修改终端的本地存储。用户按下发送键前主机不会产生任何中断。不可能实现Unix原始模式(raw mode)下那种字符层面上的交互。
TSO是和Unix交互环境最近似的等价物,自身能力非常有限。对于系统其它部分来说,每个TSO用户都是模拟批作业。这个设施非常昂贵——太贵了,主要限于开发者和系统维护者使用。仅仅需要通过终端运行应用程序的普通用户几乎从不使用TSO。相反,他们通过事务监视器工作。这是一种多用户应用服务器,可以进行协作式多任务处理并支持异步输入/输出。从效果上来说,每种事务监视器都是一个专用的分时插件(和运行CGI的Web服务器很像,但不完全一样)。
面向批处理体系所带来的另一个后果就是生成进程非常缓慢。I/O系统有意用较高的准备成本(及其带来的延迟)来换取更好的吞吐能力。这些选择对于批处理操作来说非常适宜,但是对于交互响应来说却是致命的。可以预见,如今TSO用户将把几乎所有的时间都花在ISPF(一个对话驱动的交互环境)上。除了启动一个ISPF实例外,程序员几乎不在原生的TSO上做任何事情。这避免了生成进程的开销,代价是引进了一个非常庞大的程序。这个程序,除了不会启动机房的咖啡壶,什么事都能做。
MVS使用机器MMU,进程有独立的地址空间,只能通过共享内存支持进程间通信,也有线程功能(MVS称之为“子任务”),但用得很少,主要因为只有用汇编语言编写的程序才能方便地使用这个功能。与此相反,典型的批处理应用是由JCL(Job Control Language,作业控制语言)粘合在一起的由重量级程序调用组成的短序列,也提供脚本功能,但却是出了名的困难和死板。每个作业里的程序通过临时文件通信;过滤器之类的东西几乎毫无用武之地。
每个文件都有记录格式,有时是隐式的(例如,JCL的内联输入文件继承了穿孔卡做法,默认为 80 字节固定长度的记录格式),但更通常的情况是明确指定。许多系统配置文件都采用文本格式,但应用程序文件通常采用特定的二进制文件。一些检查文件的通用工具出于迫切需求才被开发出来,但这依然还是一个难以解决的问题。
文件系统的安全性在最初设计中根本未予考虑。然而,当人们发现安全性十分必要时,IBM以一种颇具灵感的方式加了进去:他们规定了一套通用安全性API,然后在处理每个文件存取请求前调用这个接口。结果是,产生了三种相互竞争的安全性程序包,各代表不同的设计理念——三种都相当好,在 1980 年到 2003 年中期始终没被攻破。这种多样性就允许用户安装时选择最适合实际安全策略的安全包。
网络功能也是后来才加进去的。网络连接和本地文件操作使用同一套接口的概念不存在;两者的编程接口相互独立而且区别很大。这的确帮助TCP/IP成为了首选网络协议,不着痕迹地挤掉了IBM原生的SNA(System Network Architecture,系统网络体系)。在2003 年,同一机器上两者都使用的情况虽然常见,但是SNA正在逐渐消亡。
除了在运行MVS的大企业内部,MVS上的轻松编程几乎不存在。这主要不在于工具自身的成本,而在于环境的成本——在往计算机系统上扔进几百万美元后,每个月为编译器花费几百美元就是小钱了。然而,在这个社区内也存在一个繁荣的自由软件文化,主要是编程和系统管理工具。第一个计算机用户组,SHARE,就是IBM用户在 1955 年成立的,到今天也依然很兴旺。
考虑到架构上的巨大差别,MVS是第一款符合单一Unix规范(Single Unix Specification)的非System-V操作系统,这件事非同寻常(但还是得看到,从Unix软件移植过来的软件往往碰到ASCII对EBCDIC字符集的麻烦)。从TSO启动Unix shell是可能的——Unix文件系统专门设置成MVS数据集格式。MVS Unix字符集是特殊EBCDIC代码页,交换了“新行”和“换行”(Unix中的“换行”对MVS就是“新行”),但是系统调用却是在MVS内核上实现的实时系统调用。
随着开发环境的费用下降到爱好者能够承受的范围,公共领域的MVS版本(版本3.8,始于 1979 年)拥有了一小群用户,人数虽少却在不断增长。这个系统及其开发工具和运行所用的仿真器,花一张CD的价钱就可以全部获得 。
MVS的目标始终定位在后勤部门。和VMS和Unix一样,MVS提前区分了服务器和客户端。后勤用户对界面复杂度不仅可以忍受,而且非常期待,因为他们愿意把昂贵的计算机资源尽可能花在需要处理的工作上而不是界面上。
VM/CMS是IBM另一个大型机操作系统。从历史来说,这是Unix的伯父;它们共同的祖先是CTSS——由MIT于 1963 年间开发出来并在IBM 7094 大型机上运行的一个系统。CTSS开发组后来又去开发了Multics,也就是Unix的直系祖先。IBM在剑桥大学组建了一个开发团队,为IBM 360/40——开发分时系统拥有分页MMU (在IBM系统上第一次)的改进型 360 系列机器。此后很多年,MIT和IBM程序员一直保持交流。新系统拥有一个与CTSS非常类似的用户界面,备有名为EXEC的shell和大量的实用程序,与Multics及后来Unix使用的实用程序非常类似。
从另一层意义看来,VM/CMS和Unix之间就像是游乐宫里的镜像。VM/CMS系统的统一性理念是虚拟机,由VM组件提供,每台虚拟机看起来就和运行其上的物理机是一样的。它们都是抢先式多任务处理,要么运行单用户的操作系统CMS,要么运行一个完整的多任务处理操作系统(如MVS,Linux或者VM自己)。虚拟机对应Unix的进程、后台程序和仿真器,它们之间的通信通过连接一个虚拟机的虚拟穿孔机和另一个虚拟机的虚拟读卡机来完成。另外,CMS内提供了一个叫作“CMS管道”的分层工具环境,直接取自Unix的管道模型,但在结构上已经扩展到可以支持多道输入和输出。
当虚拟机之间的通讯还没明确建立时,它们是完全隔绝的。操作系统具有和MVS一样的高可靠性、伸缩性和安全性,而且灵活性和易用性比MVS要好得多。另外,CMS中类似内核的部分不需要得到VM组件的信任,对它的操控是完全隔离的。
尽管CMS是面向记录的,但这些记录实际上等价于Unix文本工具所用的行。CMS的数据库更好地集成到CMS管道中,而Unix中的大多数数据库都独立于操作系统。近年来,CMS已经扩展到完全支持单一Unix规范。
CMS采用交互式和会话式UI风格,和MVS相差很远、但和VMS、Unix近似,大量使用一个叫XEDIT的全屏幕编辑器。
VM/CMS出现在客户端/服务器的区分之前,现今和IBM模拟终端一起几乎完全作为服务器操作系统使用。在Windows主宰桌面市场之前,VM/CMS不仅在IBM内部、而且也为大型机客户站点提供字处理服务和电子邮件服务——实际上,由于VM早就有提供成千上万用户的伸缩性,许多机器专门安装VM系统,只用它运行这些应用程序。
Rexx脚本语言支持编程的风格和shell、awk、perl或python有几分相似。因此,轻松编程(特别是系统管理员的轻松编程)在VM/CMS上非常重要。由于允许自由流通,管理员通常更愿意在虚拟机上而不是直接在裸机上运行产品级MVS,因此,人们很容易获得CMS并充分利用其灵活性(有一些CMS工具可允许访问MVS文件系统)。
VM/CMS在IBM中的历史同Unix在数字设备公司(DEC,他们生产了首次运行Unix的硬件)中的历史惊人地相似。IBM花了数年时间才明白自己的非正式分时系统的战略意义,与早期Unix社区行为非常类似的VM/CMS编程者社区就在那时兴起了。这些编程者分享想法和对系统的发现,最重要的是他们分享实用工具的源码。尽管IBM多次试图宣布VM/CMS结束,但这个社区——包括IBM自己的MVS系统开发者——坚持维持这个系统的存活。VM/CMS甚至也经过和Unix同样的循环,从事实上的开源到闭源,再回到开源——只不过没有Unix开源那样彻底罢了。
然而,VM/CMS所缺乏的是一个像C语言那样的东西。VM和CMS都用汇编语言编写,而且一直如此。和C最像的是PL/I的各种删节版,IBM用其进行系统编程,但从来没提供给客户。因此,尽管 360 系列已经升级到 370 系列、XA系列,最后到现在的z系列,这个操作系统却仍然截止在最初架构的框框中。
自 2000 年以来,IBM以前所未有的力度在大型机上推广VM/CMS系统——作为能同时容纳成千上万虚拟Linux机的手段。
Linux由Linus Torvalds于 1991 年发明,是 1990 年后出现的新学派开源Unix阵营(也包括FreeBSD、NetBSD、OpenBSD和Darwin)的领头羊,代表了整个阵营的设计方向。Linux的技术趋势可视为整个阵营的典型。
Linux并不含任何来自原始Unix源码树的代码,但却是一个依照Unix标准设计、行为像Unix的操作系统。在本书的其余部分,我们重点强调的是Unix和Linux的延续性。无论从技术还是从关键开发者两个方面看,这种延续性都极其紧密——但此处,我们的重点是介绍Linux正在前进的几个方向,这些也正是Linux开始与“经典”Unix传统分道扬镳的标志。
Linux社区的许多开发者和积极分子都有夺取足量桌面用户市场份额的雄心壮志。这就使Linux的目标受众比“旧学派”Unix广泛得多,后者主要瞄准服务器和技术型工作站市场。这一点影响了Linux黑客设计软件的方式。
最明显的变化就是首选界面风格的转变。最初,设计Unix是为了在电传打字机和低速打印终端上使用。Unix生涯的大多数时间被用在字符型视频显示终端上,没有图形和色彩能力。大多数Unix程序员仍然固执地坚持使用命令行,即使大型终端用户应用程序很早就已经移植到基于X的GUI中了。这种状况也一直体现在Unix操作系统及应用程序的设计中。
另一方面,Linux的用户和开发者不断自我调整来消弭非技术用户对CLI的恐惧。他们比旧学派Unix、甚至同时代专有Unix更看重GUI及其工具的开发。其它开源Unix也在发生同样变化,变化虽小,但意义深远。
贴近终端用户的愿望使得Linux开发者比专有Unix更注重系统安装的平稳性和软件发布问题。由此产生的结果就是Linux的二进制包系统远比专有Unix的类似系统复杂,所设计的界面(2003 年只取得部分成功)更合乎非技术型用户的口味。
Linux社区比旧学派Unix社区更希望将他们的软件变成能够联接其它环境的通用渠道。因此,Linux的特色就是能支持其它操作系统特有文件系统格式的读(更常见的是)写以及联网方式。Linux也支持同一硬件上的多重启动,并在Linux自身的软件中进行模拟。Linux的长期目标是包容;Linux模拟的目的就是为了吸收 。
包容竞争者的目标加上贴近终端用户的动力,促使Linux开发者广泛吸收非Unix操作系统的设计理念,甚至到了使传统Unix显得十分孤立的地步。Linux应用程序采用Windows的.INI格式文件进行配置是一个小例子,我们将在第 10 章予以讨论。Linux 2.5采纳了扩展文件属性,加上其它一些特性,就可以模仿Macintosh的“资源分叉”语义。这也是写作本书时最近的一个重要例子。
但是,Linux给出“因为没安装对应的软件,所以打不开文件”这种Mac式诊断之时,就是Linux不再是Unix之日。
—Doug McIlroy
其余的专有Unix(如Solaris、HP-UX、AIX等)都是为庞大IT预算设计的庞大产品。人们愿意掏钱努力优化,追求在高端的先进硬件上达到最大效能。因为很多Linux部件源自PC爱好者,所以强调用尽量少的资源做尽可能多的事。当专有Unix牺牲在低端硬件上的性能而专门为多处理器和服务器集群调优时,Linux核心开发者的选择很明确:不能为在高端硬件上获得最大性能收益,而在低端机器上增加复杂度和开销。
事实上,不难理解Linux用户社区中相当一部分人要从过时了的硬件中榨取有用东西的做法,就像 1969 年Ken Thompson对PDP-7 一样。因此,Linux应用程序不得不始终保持瘦小精干的体态,而这是无法在专有Unix下的应用软件中体验到的。
这些趋向对Unix整体的发展产生了影响,我们将在第 20 章回顾这个话题。
我们做过尝试,选择一个现在或者过去同Unix一争高下的分时系统进行比较,但似乎能入围者并不多。大多数(Multics、ITS、DTSS、TOPS-10、TOPS-20、MTS、GCOS、MPE还有其它不下十几种)操作系统早已消亡,已渐渐从计算机领域的集体记忆中淡出。在我们已经讨论的操作系统中,VMS和OS/2 也已濒临死亡,而MacOS已经被Unix的派生系统所收纳。MVS和VM/CMS仅仅局限于单一的专有大型机领域。只有独立于Unix传统外的Microsoft Windows系统还算是一个真正活着的竞争对手。
我们在第一章说明了Unix的优势,那当然是问题的部分答案;然而,把问题反换一下:究竟Unix竞争者的什么劣势让它们失败,其实更有说服力。
这些竞争对手最明显的通病是不可移植性。大部分 1980 年前的Unix竞争者都被拴到单个硬件平台上,随着这个硬件的消亡而消亡。为什么VMS可以坚持这么久?值得我们作为案例研究一个原因是:VMS成功地从最初的VAX硬件移植到了Alpha处理器(2003 年正从Alpha移植到Itanium上)。MacOS也在 1980 年代后期成功完成了从摩托罗拉 68000 到PowerPC芯片的迁跃。微软的Windows处在计算机商品化将通用计算机市场扁平化到单一PC文化的时期,真是生逢其时。
自 1980 年起,对于那些要么被Unix压倒要么已经先Unix而去的其它系统,不断重现的另一个特有弱点是:不具备良好的网络支持能力。
在一个网络无处不在的世界,即使为单个用户设计的系统也需要多用户能力(多种权限组)——因为如果不具备这一点,任何可能欺骗用户运行恶意代码的网络事务都将颠覆整个系统(Windows宏病毒只是冰山一角)。如果不具备强大的多任务处理能力,操作系统同时处理网络传输和运行用户程序的能力将被削弱。操作系统还需要高效的IPC,这样网络程序彼此能够通信,并且能够与用户的前台应用程序通信。
Windows在这些领域具有严重缺陷却逃脱了惩罚,这仅仅因为它们在连网变得真正重要以前就形成了垄断地位,并拥有一群已经对机器经常崩溃和无数安全漏洞习以为常的用户。微软的这种地位并不稳定,Linux阵营正是利用这一点成功地(于 2003 年)在服务器操作系统市场取得了重大突破。
在个人机刚刚进入全盛时期的 1980 年左右,操作系统设计者认为Unix和其它传统的分时系统笨重、麻烦、不适合单用户个人机这个美丽新世界,而弃之不理——根本不顾GUI接口往往要求改造多任务处理能力,来适应不同窗口及其部件的绑定执行线程的事实。青睐客户端操作系统的趋势非常强烈,服务器操作系统就像已经逝去的蒸汽机时代的遗物一样遭到冷落。
但是,正如BeOS设计者们所注意到的那样,如果不实现某些近似通用分时系统的东西,就无法满足普遍联网的要求。单用户客户端操作系统在互联网世界里不可能繁荣。
这个问题促使客户端操作系统和服务器操作系统重新汇到了一起。首先,互联网时代之前的 1980 年代晚期,人们首次尝试通过局域网进行点对点联网,这种尝试暴露了客户端操作系统设计模式的不足:网络中的数据必须放到集合点上才能实现共享,因此如果没有服务器就做不到这一点。同时,人们对Macintosh和Windows客户端操作系统的体验也抬高了客户所能容忍的最低用户体验质量的门坎。
到了 1990 年,随着非Unix分时系统模型的实际消亡,客户端操作系统设计者还是拿不出来多少可能解决这一挑战的方案。他们可以吸收Unix(如MacOS X所做的),或通过一次一个补丁重复发明一些大致等价的功能(如Windows),或试图重新发明整个世界(如BeOS,但失败了)。但与此同时,各种开源Unix的类客户端能力不断增强,开始能够使用GUI并能在廉价的个人机上运行。
然而,这些压力在两类操作系统上并未达到上面描述所意味的那种对称。将服务器操作系统特性,如多用户优先权组和完全多任务处理,改装到客户端操作系统上非常困难,很可能打破对旧版本客户端的兼容性,而且通常做出的系统既脆弱又令人不满意,不稳定也不安全。另一方面,将GUI应用于服务器操作系统,所出现的问题却大部分可通过灵活处理和投入更廉价硬件资源得到解决。就像造房子一样,在坚实的地基上修理上层建筑当然要比更换地基而不破坏上层建筑来得容易。
除了拥有与生俱来的服务器操作系统体系优势外,Unix一直不明确界定自己的目标受众。Unix的设计者和实现者从不自认为已经完全清楚Unix的所有潜在用途。
因此,与之竞争的客户端操作系统把自己改造成服务器操作系统,Unix比起来更有能力把自己改造成客户端操作系统。尽管 1990 年代Unix的复苏有多方面的技术和经济因素,但正是这一点,使Unix在前述所有操作系统设计风格的讨论中最为抢眼。