重新审视《Unix 痛恨者手册》(Raymond, Eric S 于 2008)
最后更新于
最后更新于
一位评论者在我预告《为什么 C++ 不是我们最喜欢的编程语言》的帖子下问道:“esr,从一个老程序员的角度来看,你认为哪些章节是好的,哪些章节是不好的?”
(技术说明:事实上,我没长胡子,如果有,也不可能是灰白色的)
这个问题很好,值得写一篇博客。我是这本书手稿的首位技术审稿人,当时它还在准备中——虽然是 IDG 出版的,但我想它是通过 MIT 出版社送到我手上的。正如我在同一评论线程中提到的,我曾努力劝说作者们在技术论证上做得更强一些,而不是单是单纯的情绪化发泄,但未成功。他们想要发泄情绪,天啊,他们就是要发泄,而没有哪个审稿人能阻止他们。
自那时以来,我一直觉得这很遗憾。当然,我是一位长期的 Unix 爱好者;否则我也不会写《Unix 编程艺术》这本书。我原本以为一本冷静地给 Unix 社区带来一些适当并且有力震撼的书会是好事;然而,许多好的观点被不合时宜的讽刺语言掩盖了。
你可以在浏览《手册》的内容。以下是我重新阅读它后的评估,按章节逐个评价,最后我会总结并作一些整体评论。
我非常尊重 Don Norman,但他写这篇引言的时候显然不在状态。故意幽默的尝试大多失败了。而“……现在这真是个自相矛盾的说法,Unix 的图形用户界面”在 2008 年看起来显得颇具讽刺意味。除此之外,内容相当贫乏。
同样不幸。这一部分为书中的许多部分定下了基调,充斥着夸张的讽刺,原本可以作为有益批评的部分。今天,在 Linux 系统都附带相当精致的 GUI 的环境中,这些讽刺显得极为过时。关于 1987 年 Sun 硬件上的终端会话的故事,现在看起来既陈旧又让人烦躁。
作者写道:“人们很容易把我们当作心生嫉妒的牢骚者,或是那些怀念被 Unix 商业成功淘汰的系统的浪漫主义者,但那样做是错误的:我们的判断很敏锐,理想很纯粹,我们的愤怒也真实。”我认识并且相当喜欢其中一些作者,所以实话说,十四年后,像他们这样写的人已经越来越让人觉得他们更像是在抱怨,而不再是冷静的批评。
Dennis Ritchie 的回应仍然很有趣,他的开头和结尾依然准确地总结了这本书的整体内容:
我屈服于你们在前言中的诱惑:我确实把你们看作是心生嫉妒的牢骚者和怀旧的记忆守护者。你们记忆中那些美好的系统 […] 不仅仅是过时了,它们正从下方为草地提供肥料。 […] 你们的书就像一个布满仔细观察的布丁,很多都想得不错。像粪便一样,它包含了足够多的未被消化的营养物质,可以支持某些生命。但它并不是一块美味的馅饼:它太散发着蔑视和嫉妒的味道。
本章内容的历史和抨击部分差不多,历史并不新颖,抨击写得巧妙,但一旦你笑过那些文字上的烟花,便没什么特别吸引人的地方。关于标准之争的内容现在看起来非常过时。
而且没有提到 Linux,尽管它在本书写作时已经获得了 TCP/IP 支持,并且有一个蓬勃发展的社区。我们不能责怪作者没有预见到 2008 年,但这感觉就像他们的思维还停留在 1980 年代,完全忽视了 1994 年的现实。
这一章有一些公正的批评。是的,Unix 命令名确实晦涩,这确实是一个用户界面问题——到 2008 年,图形用户界面的出现已经很大程度上缓解了这个问题,但它依然是个问题。
文档质量仍然不太好,而且当命令执行失败时,能提供清晰和有用的反馈是 Linux 应用可以大大改进的地方。但这些问题几乎存在于所有地方,而不仅仅是 Unix 领域;因此,把 Unix 归咎于这些问题似乎有些不公平。
在其他一些问题上,作者表现得不够好。“命令行为的一致性和可预测性,以及选项和参数的解释方式”听起来很不错,但第一部分是不可能的(不同命令必须有不同的行为,因为它们解决的问题不同),第二部分更多是抱怨 shell 的通配符扩展,而不是其他问题。抱歉,这个问题太有用了。如果你不喜欢 rm *
的工作方式,那就去启动图形文件管理器吧。
然后是很多关于 Unix 开发者貌似满足于编写粗制滥造程序的攻击。这既不真实也不有趣,只是更多的夸张讽刺,掩盖了他们本该有的观点。
这一章里有一些值得一提的内容,但总体而言,挖掘这些有价值的部分似乎不值得在这些垃圾堆中寻找。
是的,man(1)仍然很笨重,man 页面仍然是参考资料,而不是教程。这在 2008 年并不是什么新闻,1994 年时也不是。这其中的区别是,在 2008 年,他们批评的 man 页面风格已经不再成为阻碍;现在我们有了网页和搜索引擎。
他们对于“源代码就是文档”的批评带有一些不经意的讽刺,因为开源运动已经发生。关于过时的 shell 的抱怨对讨论几乎没有什么贡献。
本章中有许多是真实的,但几乎没有什么在 2008 年仍然有用或新颖的内容。
这主要是对 sendmail 的吐槽。大部分批评是有道理的。现在许多 Linux 发行版默认使用 Postfix,而且这一比例在不断增加;故事到此为止。
USENET 并未完全死去,但如今它主要是 p2p 媒体共享和色情内容的中继频道。这一章对黑客历史学家来说有一些有趣的内容,但对当前的环境没有什么相关性。
这一章已经变得非常过时。几乎可以说,现在已经没有实际的 VDT(视频显示终端)了;虽然一些过时的销售点系统中还偶尔能见到,但也就只有这些。现在全是终端仿真器或者操作系统的控制台驱动,它们都使用 VT100/ANSI,故事到此为止,问题也就解决了。
在这段嘲讽之中有一个关于架构的尝试。是的,如果 Unix 内核能提供一个统一的屏幕绘制 API,而不是把这项工作交给像 curses(3) 这样的用户空间库,那确实会很好。但——作为专家来说,我曾参与实现 ncurses(它是 curses 的开源仿真)的大部分功能——将所有这些复杂性移到内核级别基本上什么也解决不了。根本问题是,Unix(不同于这些作者在浪漫化怀念的早期系统)需要与大量没有向系统自我标识的 VDT 进行通信(因此无法自动配置它们),并且不同类型的 VDT 具有复杂不同的命令集。curses 做的事必须存在某个地方,而它的能力数据库;把它放到用户空间的服务库中,至少保证了这个相当棘手的代码中出现的错误不会崩溃整个系统。
但这些都是过去的问题;VDT 已死,它们抱怨的那些问题也随之死去。
这一章的开头不太好,抱怨 X 的性能和内存使用在与 14 年后的应用程序比较时显得相当老派。接着是对 1990 年代 X 应用程序稀缺性的批评,当你在 Linux 桌面上使用 evince 阅读这本书,并同时运行我正在写作的 Emacs 实例,旁边还有一个包含十几个小程序的工具栏,以及一个 Web 浏览器时,这些批评显得格外具有讽刺意味。
我认为,作者对 X 机制/策略分离原则的拒绝在基础上是错误的。我在《Unix 编程艺术》中曾论证过,这一原则赋予了 X 适应新技术和新 UI 思维的能力,而这一点是其他竞争者从未做到的。我现在仍然认为这是对的。
不过,并不是这一章所有的批评都误入歧途;Motif 确实糟糕透顶,它死了也好。ICCCM 也确实如作者所描述的那样糟糕,但现在已经不那么引人注意了,因为现代的工具包和窗口管理器做得很好,能够将这些丑陋的部分隐藏起来,不再让应用程序感受到它们的存在。
虽然没有明确提到,但我几乎可以确定这一章的大部分内容是 Don Hopkins 写的。Don 是一位非常聪明的黑客,也是一个好人,他曾在 Sun 的 NeWS 项目上投入了大量精力,但最终在 X 的碾压下败下阵来。这一章最适合从他在 1980 年代末亲口对我说出的那段关于 NeWS 的愤慨中去解读。
Don 在架构上可能是对的。但 X 的胜利并非偶然;它之所以击败 NeWS,基本上是因为 X 是开源的,而 NeWS 不是。1987 年之后的 20 年里,正是因为有足够多的人投入足够多的工作,X 才得以修复,特别是在 Keith Packard 于 2001 年回归并彻底重写渲染核心后。现代的工具包基本上绕过了那个可恶的资源系统。X 扩展地狱和设备可移植性问题,这些作者所愤慨的东西,最终成了一个暂时现象,因为人们仍在致力于理解 2D 图形问题领域。
话虽如此,Olin Shivers 对 xauth 的批评仍然相当有趣,我很高兴这些年来我都没有需要使用它的情况。
在这一章的前半部分,作者对“无数不兼容的 Shell”进行了解剖(包括章标题中的 csh),其中大多数已经基本上消亡;bash shell 赢了。因此,这一章的大部分内容实际上只是考古学,对于像我这样的老家伙来说或许有点意义,但对于现代的 Linux 或 Unix 用户来说,就像是托勒密天体说的外圈。
在 Shell 编程中的可移植性问题如今几乎不再是问题。像 Perl 和 Python 这样的语言已经取代了作者批评的那种脆弱的 Shell 脚本——实际上,这就是它们之所以被称为“脚本语言”的原因。作者确实预见到了这一发展:
“冒充跨星际空间的无望梦想守护者,我们提出正确的模型是过程调用(无论是本地还是远程),在一种允许一等结构(C 语言在其青春期获得的)和函数组合的语言中。”
我给他们点个赞,他们确实在这一点上是对的。不过值得注意的是,他们似乎对 Perl 并不知情;在 1994 年时,Perl 已经在公开场合提供了这种功能了。
关于 find(1)
的章节结尾的咆哮依然有趣。
作者在这一章过于匆忙地将 Unix 工具箱视作一个原始的玩具箱,这个偏见错误却给了 Unix 程序员一个很好的借口,忽视作者所指出的那些正确的部分。举一个相对较小的例子,make 中使用制表符确实是个错误,应该作为工具设计师的一个可怕教训。
更普遍地说,他们关于 C 语言及其相关假设和工具链的许多观点是有道理的。是的,所有那些固定长度缓冲区的假设是工具弱点和由此产生的坏习惯的指责。是的,LISP 会是一个更好的选择。是的,异常捕获是非常重要的。
渐渐地,在一个混乱的、进化的过程中,Unix 社区正在自我教授这一章的作者们想要传达的教训。我同意他们的观点,虽然这个过程应该发生得更快一些,应该早点发生。
如果不是因为下一章,我会说这一章是书中最不显得过时的部分。
尽管在一些小方面已经过时(C++ 在 1994 年后增加了命名空间,作者也未能涉及模板,因为模板当时尚未加入),这章依然锐利地指出了 C++ 的问题。唯一的重大错误是,作者假设 C++ 属于 Unix 传统的主流,且被 Unix 程序员广泛采纳,因此它是 Unix 的罪状之一。开源托管网站如 SourceForge 和 Freshmeat 上的语言使用统计数据显示,事实并非如此。
这一章是对过去的回忆。写这本书时,人们仍然实际使用磁带进行备份。这大概是书中最为过时的章节。
到了 2008 年,我七十多岁的母亲使用 Linux 机器,在她适应的过程中打了几次电话后,我已经习惯几个月都不听她提这事了。足以说明问题。
这一章中的许多技术批评仍然是有效的,因为 Unix 系统依然表现出这些行为并且存在这些漏洞。但从另一个层面来看,这一章似乎陷入了一个奇怪的空白区;作者无法指出一个安全模型更好或安全记录更好的操作系统。他们甚至没有尝试写出他们想象中这种系统的样子。
与第 9 章和第 10 章的对比很有启发性。许多作者来自于计算机语言(LISP、Scheme 及相关语言)的一种传统,在许多重要方面,它们优于 1994 年时的 Unix 本地语言(这段差距现在已经有所缩小)。他们知道什么是比较卓越的,因此可以从现实的基础上进行批评。
然而,作者无法以类似的方式建议如何从根本上改进 Unix 的安全模型和工具。这是因为,尽管 Unix 存在许多缺陷,但迄今为止,没有人发现并成功部署一个更好的模型。像基于能力的操作系统这样的实验性操作系统仍然很吸引人,但它们并不是解决方案。
对这一章的正确回应是:“你说得对。那么,然后呢?”
这一章中对 Unix 文件系统性能和可靠性的许多批评如今已经完全过时。我们已经学到了硬化和日志记录;可怕的 fsck
恢复会话的时代已经过去。对于不可靠和重复的锁机制的抱怨也已经过时,标准委员会在这方面做了不少好工作。
不过,作者对无装饰的字节包模型的批评并非完全没有道理;正如语言一样,作者中的一些人有真实世界的经验,使用过支持更丰富语义的系统,知道自己在说什么。
一些 Linux 文件系统黑客似乎在摸索一种模型,其中文件是可传输单元,可以流式传输,但在内部拥有类似文件系统的结构,能够嵌入元数据和多个数据叉。其他人则尝试了类似 BeOS 的数据库视图。
这里可能还有进展可作。然而,遗憾的是,这种进展不会通过作者习惯于在一磅的嘲弄中埋藏一盎司见解来得到帮助。
本章中描述的一些具体 bug 已经修复,但其中对 NFS 架构的许多批评仍然有效(至少在我上次仔细研究 NFS 时,这些批评仍然成立)。这一章仍然具有启发性。
最初的问题是:“你认为哪些章节好,哪些章节不好?”让我们来分类一下。
对于现代读者来说,书中最过时且缺乏内容的章节大概是第 11 章(系统管理)、第 5 章(无聊的网络)、第 6 章(终端错乱)、第 4 章(邮件)和第 1 章(Unix:世界上第一款计算机病毒),按从最差到最不差的顺序排列。
讲解最有条理、仍然有很多教训值得汲取的章节无疑是第 10 章(C++),紧随其后的是第 14 章(NFS)。
有一些章节大部分是嘲讽或者过时的内容,但其中埋藏了一些有价值的教训。按重要性降序排列:
作者在第 8 章中指出,经典的 shell 脚本是脆弱且令人不悦的,应该用支持数据结构和真实过程组合的语言来替代;这一点实际上已经在很大程度上实现了。
第 9 章(编程)中对 C 的批评是正当的。
第 13 章(文件系统)中对纯字节包模型的反对应该引发一些深思。
最底层是第 2 章(欢迎新用户!)中关于命令名繁琐和选项过多的一些宝贵建议。
一些章节告诉我们关于 Unix 的负面真相,但只是重复了那些(a)Unix 社区内已经广为人知的问题,且(b)这些问题在 Unix 社区之外也没有得到解决。作者的发泄或许让他们感到好受些,但这种做法并没有为解决问题作出贡献。我肯定会把第 12 章(安全性)和第 3 章(文档?什么文档)归类到这一类。
第 7 章(X-Windows 灾难)对我来说是最难分类的章节。尽管他们提到的某些地方仍然存在尴尬之处,但我认为他们错误地断言整个系统在功能上糟糕透顶,并且错误地批评了该系统的架构和设计哲学。
我现在比以往任何时候都更认为这本书是一次错失的机会。自 1994 年以来的 14 年已经足够长,能够吸收并整合有用的教训;如果所有章节都达到了第 10 章或第 14 章的水平,或许我们今天的 Unix 会更好。遗憾的是,作者更愿意展示一些挑衅性的言辞,而不是启动建设性的对话。
我们没得到 LISP,但我们得到了 Python。当然,我也可以提到 Perl 和 Tcl,但它们离 LISP 更远(参见 Peter Norvig 的详细论证,)。我的意思并不是要提倡 Python,而是想指出,Unix 社区注意到了 C 的不足,并解决了这个问题。如果相信 Freshmeat 上的统计数据,现在更多的新项目是用脚本语言开始的,而不是 C。