2008年4月29日星期二

奇文-闲话操作系统

VAX是一种32位的小型机,因为它惊人的寻址能力(达4GB,尽管由于成本关系,大多数VAX机器也只配备了1M内存),DEC为它设计了革命性的换页技术,的确让Unix有点赶不上趟,但这项让DEC引以为傲的技术优势并没有保持多久,仅仅一年之后,一个叫做Bill Joy的来自Berkeley(加州大学伯克利分校,University of California at Berkeley)的学生就在Unix上实现了这一技术。

  Berkeley是一所很牛的大学,做为一所州立大学,它在很多领域都能和那些财大气粗的私立学校相抗衡,它不仅拥有数以十计的诺奖得主,而且还是许多社会潮流的源头,比如嬉皮文化、反越战运动等。在九十年代末的一次美国大学评比中,它的不少专业都名列前茅,比如文学、新闻等,当然,更值得说的是,它的电脑和电子工程技术专业名列全美第一,甚至把更鼎鼎大名的MIT、Stanford都甩到了后面。Berkeley与Bell Labs似乎关系不错,早在Thompson发表那篇宣言般的论文之前,就搞了一份Unix来用,还专门为此买了一台PDP-11/45,比较丢人的是,他们鼓捣了几天也没把Unix装上去,最后还得劳动Thompson亲自出马,呵呵。其实原因是出在Unix自身,因为他们买的那台PDP太高级,竟然配备了双硬盘,Unix那时还不支持。
  其实早在此之前,Joy就已经致力于Unix的研发。在1997年年初,Joy就编写并发布了一个以BSD(Berkeley Software Distribution)命名的Unix版本(有个传说,说最初Joy用三天时间就写成了一个简陋但可用的Unix,忍不住怀疑搞操作系统的都是一伙超人~~~~:),BSD后来影响如此之大,足可与AT&T的原版相提并论。在第一版的BSD中,Joy就搞出了些新意思,他写了Unix中的第一个Pascal编译器(Pascal由瑞士的Niklaus Wirth教授于1970年发明,是一种影响深远的语言,许多现代语言,例如Java都受过它的影响,尤其以应用于教学著称,当然也应用于许多实际的项目,著名的如Delphi,它甚至被用来写操作系统,Apple早期的MAC系统就是用他们自己的特殊版本的Pascal写的),以及Unix中的第一个全屏编辑器VI(到现在仍是Unix中最常用的程序之一,虽然编辑器出了无以数计,但能与VI相提并论的,大概也只有Emacs了,这又关涉到另一个大牛人,先按下不表:),另外,他还写了一个重要的C语言函数库termcap(也流行了好多年)。

AT&T于1983年发布了System V(简称SVR1,意为System V的第一版,在Bell Labs原版的基础上吸收了一些来自其它版本的技术),此后便一直沿用这一名称。直到1993年AT&T将UNIX卖给Novell之后(连同拥有UNIX产权的子公司USL,Unix System Laboratories),Novell仍使用了一段时间,最后一个System V版本是Novell于1993年12月推出的SVR4.2MP(MP是“多处理器”的意思)。而最先的那个版本(以Version x命名)Bell Labs仍继续发展了一段时间,直至1990年推出的Version 10。但SV系列推出后,这一系列影响就小了,大多数UNIX的变种都是源自SV系列和BSD系列,而SVR4本身就吸收了BSD系列的一些东西(主要是 TCP/IP协议栈)。

  UNIX技术虽然广受欢迎,但这一商标本身似乎并未创造很大的价值,AT&T大概是因为受条约限制,据说在它身上只赚了几亿美元,而 Novell也没能很好地利用它,Novell后来将自己的Netware与SVR4相结合,推出了Unixware,随后就将UNIX的商标权卖给了 X/Open Company。到了1995年,干脆把Unixware也卖给了SCO(Santa Cruz Operation,这家公司好象不太厚道,前两年因为Novell转向发展Linux,SCO以不太站得住脚的原因起诉Novell侵权,同时还告了 IBM,因为诉讼对Linux非常不利,激起公愤,我记得当时《电脑报》以很少见的激烈言辞在头版予以报道)。

  X/Open Company在1996年与OSF (Open Softare Foundation)合并,成立了The Open Group。这个非赢利组织到目前为止拥有着UNIX的商标权,实际上,经过这么一番复杂的变迁,UNIX这一名称似乎已经谈不上有多大的商业价值了,更多的是一种标准和象征。The Open Group负责发布和维护Single Unix Specification(UNIX规范),后来ISO(国际标准化组织)发布的UNIX标准主要就是照这个来的。关于UNIX还有一个重要的标准,就是由IEEE(电气和电子工程师协会)发布的POSIX(Portable Operating Systems Interfaces for Computer Environment,Linux最初就是照着这个葫芦画瓢来实现与UNIX的兼容的:)

有关AT&T的叙述告一段落,再说两句Thompson和Ritchie,与这两位牛人告别:)。就象顶尖的物理学家只对大问题感兴趣一样(一般是在证明一种理论后就转向研究其它,那些细枝末节的问题就交给那些实验物理学家了),在发明UNIX之后,他们并没有在这上面花费太多功夫,除了做一些本职工作的研究之外(例如IP电话),他们还继续对操作系统进行探索。由于UNIX的核心设计完成的如此之早,Thompson他们虽然天纵英才,却也没能料到计算机的发展如此日新月异,UNIX最初是作为一个基于单机的分时系统来设计的,这显然不符合当今潮流,虽然仗着它高效、稳固以及灵活的可扩充性设计,在今天的网络时代仍挑着大梁,但在新兴应用层出不穷的情形下不时表现出的核心技术的滞后让Thompson他们颇为不爽,于是他们决心搞一个新玩意。

  在80年代后期,Thompson和Ritchie开始开发Plan9(1995年推出)。这个看上去与UNIX颇为相似、仍旧以C语言编写的操作系统是全新的,它面向网络和分布式计算,在一定程度上,它提出的概念有点类似上世纪末Oracel和SUN他们提出的NC(NetWork Computer),或者叫“瘦客户端”,核心理念就是把网络里的运算能力集中起来,客户机则用不着太快。在Plan9系统的网络里,有一个“CPU服务器” ,它来处理大多数的运算请求,然后将结果返回到各客户端。这一构想很早就有人提出,所以Thompson他们也好,Oracel他们也好都不算是发明创造,在今天,这种应用也很普遍(比如Google,海量信息的搜索和查询请求的处理就是在服务器端完成的,大多数网络开发技术都是基于服务器端),但在系统级的架构上实现这一理念,Plan9首开先河。而且令人兴奋的是,Plan9顺应了开源软件的大潮,大家可以去Bell Labs或者Vita Nuova(经营Plan9和Inferno的公司)的网站上下载到它附有源码的版本。

  后来,Thompson和Ritchie还主持编写了一个叫做Inferno的系统(1996年推出),这个系统基于一种叫做Limbo的语言(这个主要是Sean Dorward设计的,Thompson曾特意声明,大家别搞错了,呵呵:),在概念上与JAVA比较类似,它有一个通过Limbo语言接口的交互层,即可以做为一个单独的操作系统安装,也可以作为一个应用程序在别的操作系统中运行——就象JAVA虚拟机(SUN也曾做过纯JAVA的操作系统,不过好象效率低下,没能推广开),Inferno比JAVA推出的略晚一些,但考虑到研发的时间因素,分不清谁先谁后,不过Thompson说Limbo要比 JAVA清爽,当然这只是一面之辞:)。

  除了计算机之外,Thompson和Ritchie的爱好就不大相同了。Ritchie喜欢古典音乐,但并不发烧,通常只在收音机里听听, Thompson喜欢流行音乐,而且非常发烧,他收集了几万首上过排行榜的流行歌曲——以一种Bell Labs发明的叫做PAC的压缩格式,音质远超MP3,而且他透露大多是“非法”取得的(相信不少同志有过类似行为:)。Ritchie喜欢旅行,规规矩矩的那种,比较出格的也就是开开大卡车,Thompson就比较猛,他喜欢开飞机,而且水平不错,在前苏联时代,曾经开着飞机过去跟他们的米格-29比试了一番。现在Thompson己经退休,当上了飞机教练,Ritchie则还在Bell Labs工作,当上了领导,负责系统软件研发部门,主要工作不再是写代码,改成了开支票(按咱们时下的流行理论,这也算是程序员修成了正果,呵呵)。 Ritchie曾经与Brian W.Kernighan合写了《The C Programming Language》,是计算机科学的经典名著(单就发行量似乎不如谭浩强那本,但影响不可同日而语),世界上最著名的程序——“Hello, World.”就是在这本书里首次亮相:)。Ritchie低调而自负,自称没有偶像,唯独对Thompson赞赏有加,他拒不承认对UNIX的贡献与 Thompson一样多,并声称Thompson的编程水平自己望尘莫及。而Thompson似乎是个乐天派,他自称做过的几乎所有事情都是出于个人爱好 ——我真希望自己也能说出同样的话来。

  1983年,Thompson和Ritchie荣获图灵奖,首开以软件工程师而不是计算机科学家的身份获奖的先例,并促使图灵奖开设了一个新奖项—— 软件系统奖;199年,他们俩又获得了“全美技术勋章”,作为程序员,他们获得的荣誉可算是登峰造极了。但Thompson对当今计算机业的看法却不太令人鼓舞,他说计算机业已经进入了中世纪,不再那么令人兴奋,他建议自己的儿子选择生物工程,而不是计算机,顺便说一句,Thompson曾经细读过 Linux的源码,对这个新一代的传奇评价不高,进而对整个计算机业的技术发展似乎都比较失望。比起二、三十年前,现如今的IT业的确商业味更浓,而革命性的创新鲜见,如果真如Thompson所言,如今是处于计算机业的“中世纪”的话,真希望能早点等到“文艺复兴”的到来:)。

转过头再来说BSD。要说BSD最大的功臣,自然非Bill Joy莫属,关于这个牛人,以及他呆了半辈子的Sun公司,有必要再说两句。

  Joy从小是个天才儿童,5岁就开始做高等数学习题——计算机牛人好象都是小时了了、大了亦佳:),比如Neumann,也是很小的时候,大概念小学的年纪,就可以玩转微积分。H.Poincare曾说数学家是天生的,不是造就的,计算机科学家也差不多,天分可能更重要一些(我可没说努力不重要)—— 15就考上大学。他考研时立志学计算机,当时计算机系最牛的几个大学,Stanford、CIT(California Institute of Technolog,加州理工学院)还有Berceley都录取了他,选择Berceley则是因为那儿的计算机设备最差,Joy觉得这样有利于自己发挥创造才能(牛人果然是与众不同:)。但Joy大概是因为忙于搞开发,Ph.D读了6年也没搞到手(拿一个正经的学位确实不容易,Linus一个硕士也读了好多年:),Joy一看,还不知道得念到什么时候,就把心一横,跟着别人开公司去了(辍学也是牛人们一个传统的说:)。

  当时搞UNIX研发的并非只有Berkeley一家,只因Berkeley出了Bill Joy这么个大牛人,一下子把别人都压下去了,到第三版(就是可以完美支持VAX机的那一版)推出后,风头之健,更是盖过了Bell Labs,成为UNIX技术发展的中心,因为这样的优势地位,不久之后,一个巨大的机遇找上门来。

  1979年,DARPA(Defens Advanced Research Projects Agency,美国国防部高级研究计划局)开始着手一项计划,全面更新他们的ARPAnet(一个用于连接他们散落在美国各地研究中心的计算机的网络,Internet的前身)。早期运行ARPAnet的都是些PDP-10,现在已是风烛残年,该退休了,DARPA准备换上新型的VAX机来接班,加上前两年开发的TCP/IP协议(Transimission Control Protocol/Internet protocol)也得借这次机会登台亮相,这将是一次脱胎换骨的大换血。DARPA首先想到的是找DEC,毕竟VAX就是他们的产品,但他们需要对操作系统进行大幅度的修改,想到DEC对于自家VMS的保守态度,上门搞不好要碰个大钉子,自己动手吧,又没下嘴的地方——正犹豫间,BSD的出现让他们眼前一亮——完全支持VAX,可获得源码,可定制,可移植,简直就是个度身定做的系统!

  于是他们找上了Berkeley。Berkeley当然是有见识的,这么有意义的活计上哪去找,共襄盛举那是求之不得啊,于是在1980年两家就签订了一个合同,由DARPA资助Berkeley发展UNIX,以满足DARPA的需要。Berkeley为此专门成立了一个小组叫做CSRG (Computer System Research Group),由Bob Fabry教授负责(就是将UNIX引入Berkeley的那位),Fabry当然很想Joy参加进来,但当时Joy刚通过Ph.D.的考试,为其学业着想,Fabry也不好意思去找他,但没多久Joy就主动找上门来了(一点不奇怪,他不参加倒真奇了怪了:)。

  且说DARPA的一个主要要求就是要将TCP/IP协议集成到UNIX中,他们此前就找了一家叫BBN的公司写好了协议栈,CSRG就安排Joy来做集成工作。没想到的是,没过几天,Joy就完成了——这没什么稀奇,稀奇的是,他根本没用BBN写的协议栈,他居然自己重写了一个,原因竟是,他嫌BBN 的那个写得太烂!要知道BBN的那玩意可是一项巨额合同的成果,BBN和DARPA这下觉得太没面子了,肯定有内情,就开会研究,请Joy参加。且说 Joy穿着件T恤,蓬头垢面的就进了会议室,满坐西装革履,看着这小子不觉有点发呆,回过神来就问他,你是怎么写出来的(估计还存着念头,以为他以前做过相关研究什么的)?没想到回答简直让他们喷血,Joy说,这个嘛,很简单,你读协议,然后编码,就行了。:)

最早集成Joy所写的TCP/IP模块的BSD版本是4.1a,跟BSD之间隔了两个版本,4.0和4.1。这两个版本的进步不算大,4.0版主要是加进去一个Lisp系统(Lisp是一种资格极老的语言,由MIT的John McCarthy教授于1958年发明,仅比史上最早的高级语言——1957年由IBM开发的Fortran——晚了1年而已,这种设计理念奇异的语言非常与众不同,它对程序与数据不加区别,对其缺乏了解的人看到它浑然一体、遍布密密麻麻括号的源程序肯定会一头雾水,但它有着强大的表达能力,尤其适合符号和文字处理,是AI(Artificial Intelligence,人工智能)领域的首选语言——目前可能是它的一个变种Scheme用得更多些,著名的AutoCAD自带的脚本语言 AutoLisp就是基于它,还有著名的Emacs也是Lisp写的,还附带一个完整Lisp版本——Emacs Lisp),以及一个增强的邮件处理系统,这个版本的生存期只有几个月,因为它存在一些严重隐患,大家抱怨颇多,于是CSRG很快就推出了4.1版,这个版本主要是对4.0版做了些修修补补,此外大一点的改动就是增加了自动配置代码功能。

  本来按Berkeley的命名规则,4.0版过后就应该是5.0版,但这时AT&T有意见了,因为他们自己推出的UNIX就叫System V,两个5容易混淆。BSD本来就是从AT&T那儿发展来的,人家一直都很宽容,况且到当时为止,AT&T的代码都还没替换干净,吃人家的嘴短,当然不好争什么,于是Berkeley决定,4字头就不往上升了,以后的版本只升小数点以后的数字(有点致敬的意思,好比芝加哥公牛队在乔丹退役后就不再使用23号球衣:)。

  而4.1a这个版本相当于现在软件的α版(内部测试版),Joy不仅为它集成了TCP/IP,还增加了多网络协议的支持能力,多个网络协议可以同时使用,这在以后成为操作系统的设计标准。因为4.0版的失败,所以CSRG决定发布新版本要慎重一点,所以先自己试用,但与现在的情形一样,这个版本还是流传了出去,并勾起了大家的兴致,不少FANS开始对4.2版充满期待。

   但4.2版的诞生颇为艰难,自1982年4月4.1a版发布开始,又过了1年零4个月,中间经历了4.1b和4.1c两个非正式版本,经历了Bob Fabry(还记得他吗?CSRG的BOSS,UNIX由他引入Berkeley,他的离去很大程度上是因为不堪重负——这种高强度的开发工作是很折磨人的)和Bill Joy(他于1982年以唯一一名技术人员的身份与Scott McNealy等4名Stanford的学生一起创办了SUN公司,如你所知,这是当今最著名的生产UNIX机器的公司,但他关于BSD的工作仍持续了好几年)的离去,才于1983年8月正式发布。

  这一版本非常成功(尽管问题也不少,主要是因为一堆新增的功能还没有很好的优化),发行量超过以往版本的总和,并且打败了AT&T的 System V,成为广大UNIX厂商制作移植版本的首选,著名的如SunOS 4就是基于它,AT&T在SV的后续开发中也集成了4.2版的许多精华(抄袭是最好的赞美,谁说的来着:)。事实上,这一版本影响如此深远,尤其是其网络模块,此后几乎所有重要的操作系统都从中借鉴,更有一些是直接拿过来用。

  除了骄人的网络功能,4.2版还有许多其它方面的重大进步,如:新的、更强大的文件系统以及磁盘配额机制,新的信号机制,以及独立出来的输入/输出系统,还有全新的文档——这些都是大动作,而且就这还是迫于发布压力进行了妥协,不然还准备对交互通讯机制和虚拟内存管理模块开刀:)。

  所有人都表示赞赏——除了BBN(就是那个写了最初的TCP/IP协议栈并被Joy给否了的公司)。他们对Joy的那一箭之仇一直耿耿于怀,于是不停地向DARPA抱怨,说自己的理念是如何先进,设计又是如何优秀,BSD如果采用了我们的模块一定会跑得更欢云云~~~~,DARPA也不禁动了心,就责成CSRG重新评估BBN的代码,CSRG的高手一看,也确实有可取之处啊,但要说把自己的代码给换掉,也未见得可行,毕竟自己的代码此时已是经过考验的,于是就提出一个折衷方案,把两套代码都包含进去,由用户选择。但这下DARPA又不同意了,这可是网络模块,搞两个政府,自立山头,不是明摆着添乱吗?坚决不行!公说公有理,婆说婆有理,这也不是办法,于是干脆,DARPA请了个第三方的专家——Ballistics实验室Mike Muus来做评估。Muus忙乎了一个月之后,结果出来了,Joy的代码通过了所有测试,而BBN的呢,在高负荷的时候时不时会死给你看:)。于是 DARPA拍板,新版本继续使用Joy的代码,BBN这下郁闷死了~~~:)。

  4.3版过了三年才发布(1986年6月),尽管间隔时间如此之长,但这一版远没有4.2版那么富于革命性,它主要是对4.2版做了些修补和优化,虽然反响不错,但也仅此而已,对业界的影响远不能与4.2版相提并论。比较值得一提的是,在发布后的晚些时候,CSRG的两个牛人,叫做Casey Leedom和Keith Bostic的,对这一版做了一次匪夷所思的移植。

  从第3版开始,BSD就是基于VAX机在搞开发,VAX是32位机,通常配有1M内存(两个内存块,每块面积有4分之1平方米,还可以配更大,寻址能力4GB,即2的32次方)及高速的磁盘(容量通常为5MB或10MB,很惊人的说~~~~),所以BSD可以实现诸如内存换页以及新文件系统等技术革新,而Leedom和Bostic现在要做的,是将4.3版移植到PDP-11上。PDP-11我们应该都比较熟悉了,UNIX的第一个正式版本就是在它上面完成的,它是DEC经典的16位机,作为那个年代流传最广的廉价计算机之一,可以理解,它拥有为数众多的FANS。但无可否认,到了1986年,它早已落后了(事实上这时连VAX机都已年满8岁,颇显老态,1年前连微处理器都发展到了32位的Intel 386),以它可怜的64K的内存寻址能力(即2的16次方,这个限制对促进UNIX短小精悍的应用程序设计风格意义重大,不得不说,硬件的简陋往往能促生优秀的软件设计,这一点在Palm系统上也得到了证明)、512K的磁盘要跑4.3BSD简直是说梦话,CSRG里的大多数人虽然都很怀疑,但还是支持他们两个试试看(这么好玩的事换我也会支持,想想看,谁要是能把Windows NT移植到286上面~~~~,呵呵)。让人大跌眼镜的是,这两个牛人不仅移植成功,居然还跑得很好——UNIX的可移植性真不是吹的:)。这个跑在 PDP-11上的新系统被命名为2.11BSD(2BSD原本是最后一个在PDP-11上开发的版本),具备和4.3BSD差不多相同的功能,很受PDP -11用户的欢迎,一直陪伴许多PDP-11工作到寿终正寝,据说直到今天,在世界上的某些角落,偶尔还可以找到跑着2.11BSD的PDP-11 (PDP-11起码也有30多岁了,不管是作为一台计算机还是作为一个电子设备,这样的高寿都很值得骄傲的说,而2.11BSD作为一个软件,20多年的寿命倒不算太难得,一些行业用户的程序往往非常古老,比如一些银行系统里大型机上的Cobol代码,有三四十年历史也不稀奇)。

4.4版的BSD于1994年6月发布,这一BSD的最终版本距离4.3版足有八年之久(Vista有望一比,巧的是,微软也吹风说Vista可能是传统Windows的收关之作,集大成者想来是得多些时间酝酿),虽然间隔如此之久,但4.4版也说不上有太大的技术进步(大概是 4.2版用力过猛,加上别的问题困扰,CSRG一直没缓过劲来),但它解决了一个很关键的非技术问题,可说是意义非凡。这八年中曲折甚多,值得一说。

  截止1986年,4.3BSD除了一个BT的PDP-11移植版外,并没有向其它机器移植,一定程度上是因为系统中有许多针对 VAX特有硬件的设计,移植并不容易,而此时VAX已日薄西山,开拓新疆域迫在眉睫,CSRG想一劳永逸地解决这个问题,于是由Joy操刀,将BSD的内核分离成两块,一块依赖于机器,一块则独立于机器,这样可以大大简化移植工作的难度。这项工作于1988年完成,CSRG发布了一个叫作4.3BSD- Tahoe(Tahoe是加州的一个高山湖,旅游胜地)的版本,于是许多移植工程就基于这一版展开了。

  这时另一个问题又暴露出来,因为BSD中包含有AT&T的代码,如果想获得BSD代码的使用权,你还得向AT&T购买一份源代码许可证,随着BSD越来越受欢迎,AT&T的价码也是越来越高(最初只要一万多美元,后来涨到好几万),许多人就开始抱怨,尤其是那些计算机公司。他们多半看中的是BSD中的网络模块,而这一部份根本不关AT&T的事(实际上AT&T自己的SVR都是用的BSD的网络代码),现在却非得去出这一份冤枉钱,实在是太亏了。于是许多人都建议CSRG把AT&T的代码去掉,把网络模块单独拿出来卖,或者干脆搞一个完全属于自己的版本(著名Hacker,自由软件的倡导者Richard Stallman早在1987年就向CSRG提出过这种建议,当然他是站在更高的立场,是为了让UNIX摆脱商业公司的束缚)。

  完全去除AT&T的代码的代码是一项大工程,所以CSRG先拿出了一个叫做Networking Release 1的东东。这不是一个完整的系统,而仅仅是一个网络模块,该版本于1989年发布,同时发布的,还有CSRG对这一版本拟定的一个许可协议。也许是受了 Stallman的GPL(General Public License)的影响,这个协议鼓励修改源码和再次发布,而且它非常宽松,比GPL宽松得多,它允许再次发布时只发布二进制版本,而不附带源码(这在 GPL中是不允许的),只需附带完整的版权声明即可(“完整”的意思是,要把该软件的渊源交待清楚,包括它来自Berkeley,以及中间还经过谁谁的修改——怎么觉得有点象纪念碑,呵呵)。后来在Networking Release 2发布时仍使用了这一协议,到4.4版发布时,这一协议演变为BSD License,后来FreeBSD又将其修改为FreeBSD License,基本上也都大同小异(因为许可太宽松,所以很多人都把FreeBSD拿过来,修改一番再拿去卖,比如前些时闹的沸沸扬扬的所谓国产操作系统“麒麟”,只是国防科技大学他们(还有其他几个公司,不一一点名)的做法未免有失厚道,不遵守版权协议也就罢了,还企图掠人之美、瞒天过海)。

  这样宽松的许可协议,等于认可一个人买下后,就可以无限制的Copy给其他人,事实上也正是这样,刚一发布,就有一些著名的FTP站点提供开始匿名下载。虽然如此,CSRG还是卖出了几百份Copy,每份定价$1000,算下来也有几十万美金,在那些财大气粗的公司看来这点小钱简直不值一提,但 CSRG的人还是很开心:)。

1991年,CSRG发布了Networking Release 2。与Networking Release 1一样,这个版本完全由CSRG的代码构成,不同的是,它的内容要比Release 1要丰富的多,接近于一个完整的系统。

  在这两年里,CSRG除发布了一个试验性质的4.3BSD- Reno之外(这个版本比较奇怪,它的主要改进是加入了来自Mach操作系统的虚拟存储模块,以及来自SUN的网络文件系统(NFS),不仅代码是采取拿来主义,连集成工作也是由来自别的大学的志愿者捉刀,Reno是Lasvegas的一座纪念建筑,用意在于表明选用这一版本的风险),大量的精力被用来剔除AT&T的代码,这一工作主要由Keith Bostic、Mike Karels和Marshall McKusick完成,事实上到Networking Release 2发布时,AT&T的代码已经被替换的差不多了,仅剩下6个核心文件没有重写。

  Release 2被CSRG当作发布完全自主版本的一个尝试,沿用了Release 1的发行方式,虽然加了很多料,但定价一样,销量也差不多。在发布6个月之后,Bill Jolitz把剩下的6个文件也重写了一遍,并把整个系统移植到386架构上(在此之前己经有BSD的PC移植版,但影响不大),发布了一个新版本,叫做 386/BSD 0.1(Linux发布之初,也采取了这样谨慎的命名方式),它完全免费,Jolitz还专门建了一个Ftp提供下载。这时PC早己是星火燎原、深入人心,可以想象,这一举措受到了热烈欢迎。

  当前流行的所有BSD版本都是源自386/BSD,但386/BSD自身发展的却并不好,照Jordan Hubbard(FreeBSD和Darwin的创立者之一)的说法,在NetBSD、FreeBSD诞生之后,它很快就象渡渡鸟一样绝迹了。 Jolitz技术上很牛X,但似乎缺乏人际交往和组织管理方面的能力,因此386/BSD的开发团队很快就分崩离析了,我看到Hubbard在一次访谈中对他抱怨颇多(人无完人,其实技术牛人出现这种状况也很常见,更著名的如William Shockely,这个大牛人、晶体管的发明者之一,甚至在当公司老板时获得了诺贝尔奖,但终因傲慢与独裁,导致手下的人才流失殆尽,本来前途无量的公司最终落得个惨淡收场,但另一方面,Fairchild和Intel倒也可说是拜他所赐 :);还有,John Carmark,这个许多人眼中最牛的程序员,在管理和沟通方面也曾陷入困境,如日中天的ID Soft差点被他整熄火,但他运气不错,最后算是跌倒了又爬起来:)。

当今流行的BSD版本主要有三个,NetBSD、FreeBSD以及OpenBSD。NetBSD资格最老(其实也只比 FreeBSD早了几个月而已),在386/BSD发布几个月后由一群爱好者组成的小组推出,它被看作是最接近传统BSD版本,拥有最佳的可移植性,支持最多种类的机器(大约有五六十种,从巨型机到手机,号称只要是有处理器的设备,它都可以支持);FreeBSD则是最为著名、也是流传最广的版本,它对 PC平台的支持最好(因此支持的平台种类也就较少),有最多的应用支持,它也是与Linux竞争最直接的Unix版本,它有一个Linux的模拟模式,在一定程度上兼容Linux程序(实际上Linux上有大量的程序都是从BSD系统上搬过来的,所以谁兼容谁倒也难说:);OpenBSD诞生较晚(1995年),它立志要当世界上最安全的操作系统(事实上也做的非常成功,但那个“最”字很难说,其实要说安全,往往与“OPEN”是对冤家,越是封闭的系统越安全,但作为通用系统,封闭又恰恰是设计的大忌,所以关于安全性的问题往往成了开放与安全之间的博弈),因此也得到了DARPA的资助(记得吗? BSD的发展很大程度上也得益于此),它对多平台的支持也很不错,但比较难用,因为它倔强的核心开发者Theo De Raadt说,我们要用更多的精力保证它更棒,而不是更美味可口:)。

  此外还有一些别的版本,比如Apple的NeXTStep(本来是Jobs自立门户期间一个野心勃勃的项目,NeXT于1996年被Apple收购, Jobs借此机会归了队,NeXTStep的设计后来被融入osX),以及MACos X的核心Darwin(尽管MACos X是一个专有系统,但Darwin本身其实是一个开源项目),还有一个2003年才推出的版本DragonflyBSD,它源自FreeBSD4.X,因为有一伙人对FreeBSD5的发展方向不满,于是就派生了这么个分支。

  以上这些现在只简单提两句,后面会作进一步介绍,回过头接着说BSD的事。

  Networking Release 2发布几个月后遇到了一点麻烦。一个新成立的公司BSDI(Berkeley Software Design)自作主张,把Networking Release 2缺少的那6个文件补了上去(应该就是直接用的 AT&T的代码),凑了一个相对完整的版本拿出来卖,价格只要$995,当然也带源码,他们做了些夸张的广告,声称比System V要便宜99%云云。于是USL(就是AT&T的那家子公司)一怒之下,就把它告了。但BSDI辩称自己用的是BSD免费发行的代码,要说有问题也就那6个文件可能有点问题,于是USL捎带着把加州大学(Berkeley是它的分校)也告上了,要求他们停止发布被认为是侵权的软件。 Berkeley也被惹火了,稍后对USL提出了反诉,借口是System V使用了BSD代码的署名权问题。

  经过一番热热闹闹、缠夹不清的诉讼程序之后(其间USL被Novell从AT&T买走,Novell对此案件的态度比较温和),两家还是坐下来用谈判解决问题,最终在1994年1月达成协议,Networking Release 2的18,000个文件中有3个被删除,还有几个作了少量修改,另有70个加上了USL的版权声明,但仍免费发放。总而言之,没怎么伤筋动骨,这场经历了两年多的热闹最终是雷声大雨点小。

  CSRG现在总算有精力干点正经事了。1994年6月,他们终于发布了4.4BSD,而且同时发行了两个版本,一个叫4.4BSD-Lite,一个叫 4.4BSD-Encumbered,这完全是因为版权问题。4.4BSD-Lite完全是自有代码(而且终于获得了USL的认可),但功能不太完整, Encumbered则是一个功能完整的版本,但仍需要用户买一份USL的源代码许可。不出意料的是,仍是那个功能单薄点、但完全自由的4.4BSD- Lite更受欢迎,它成为BSDI、NetBSD、FreeBSD等发行版本新的代码来源。

  1995年6月,4.4BSD-Lite release2发布,从版本号就看得出来,这是一个缝缝补补的版本,实际上这期间CSRG已经没怎么搞开发,仅仅有一个人在兼职搞善后罢了。 release2发布的同时,CSRG宣告解散,官方说法是,BSD已发展的相当成熟,对于操作系统的研究目标已经达到。十五年的光辉岁月,大量牛人出没其中,并在此扬名立万,CSRG这个名字在计算机历史上已堪称不朽。

2008年4月28日星期一

C语言

定义
C语言是一种计算机程序设计语言。它既有高级语言的特点,又具有汇编语言的特点。它可以作为系统设计语言,编写工作系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序。因此,它的应用范围广泛。

C语言对操作系统和系统使用程序以及需要对硬件进行操作的场合,用C语言明显优于其它解释型高级语言,有一些大型应用软件也是用C语言编写的。

C语言具有绘图能力强,可移植性,并具备很强的数据处理能力,因此适于编写系统软件,三维,二维图形和动画。它是数值计算的高级语言。

常用的C语言IDE(集成开发环境)有Microsoft Visual C++,Borland C++,Watcom C++ ,Borland C++ ,Borland C++ Builder,Borland C++ 3.1 for DOS,Watcom C++ 11.0 for DOS,GNU DJGPP C++ ,Lccwin32 C Compiler 3.1,Microsoft C,High C,Turbo C等等......




C语言的发展历史

C语言的原型ALGOL 60语言。(也称为A语言)

1963年,剑桥大学将ALGOL 60语言发展成为CPL(Combined Programming Language)语言。

1967年,剑桥大学的Matin Richards 对CPL语言进行了简化,于是产生了BCPL语言。

1970年,美国贝尔实验室的Ken Thompson将BCPL进行了修改,并为它起了一个有趣的名字“B语言”。意思是将CPL语言煮干,提炼出它的精华。并且他用B语言写了第一个UNIX操作系统。

而在1973年,B语言也给人“煮”了一下,美国贝尔实验室的D.M.RITCHIE在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言。

为了使UNIX操作系统推广,1977年Dennis M.Ritchie 发表了不依赖于具体机器系统的C语言编译文本《可移植的C语言编译程序》。即是著名的ANSI C。

1978年Brian W.Kernighian和Dennis M.Ritchie出版了名著《C语言程序》(The C Programming Language),从而使C语言成为当时世界上流行最广泛的高级程序设计语言。

1988年,随着微型计算机的日益普及, C语言出现了许多版本。由于没有统一的标准,使得这些C语言之间出现了一些不一致的地方。为了改变这种情况,美国国家标准研究所(ANSI)为C语言制定了一套ANSI标准, 成为现行的C语言标准3.C语言的主要特点 。C语言发展迅速, 而且成为最受欢迎的语言之一, 主要因为它具有强大的功能。许多著名的系统软件, 如DBASE Ⅲ PLUS、DBASE Ⅳ 都是由C 语言编写的。用C语言加上一些汇编语言子程序, 就更能显示C语言的优势了,象PC- DOS 、WORDSTAR等就是用这种方法编写的。



C语言的优点


1. 简洁紧凑、灵活方便

C语言一共只有32个关键字,9种控制语句,程序书写自由,主要用小写字母表示。它把高级语言的基本结构和语句与低级语言的实用性结合起来。 C 语言可以象汇编语言一样对位、字节和地址进行操作, 而这三者是计算机最基本的工作单元。

2. 运算符丰富

C的运算符包含的范围很广泛,共有种34个运算符。C语言把括号、赋值、强制类型转换等都作为运算符处理。从而使C的运算类型极其丰富表达式类型多样化,灵活使用各种运算符可以实现在其它高级语言中难以实现的运算。

3. 数据结构丰富

C的数据类型有:整型、实型、字符型、数组类型、指针类型、结构体类型、共用体类型等。能用来实现各种复杂的数据类型的运算。并引入了指针概念,使程序效率更高。另外C语言具有强大的图形功能, 支持多种显示器和驱动器。且计算功能、逻辑判断功能强大。

4. C是结构式语言

结构式语言的显著特点是代码及数据的分隔化,即程序的各个部分除了必要的信息交流外彼此独立。这种结构化方式可使程序层次清晰, 便于使用、维护以及调试。C语言是以函数形式提供给用户的,这些函数可方便的调用,并具有多种循环、条件语句控制程序流向,从而使程序完全结构化。

5. C语法限制不太严格,程序设计自由度大

虽然C语言也是强类型语言,但它的语法比较灵活,允许程序编写者有较大的自由度。

6. C语言允许直接访问物理地址,可以直接对硬件进行操作

因此既具有高级语言的功能,又具有低级语言的许多功能,能够象汇编语言一样对位、字节和地址进行操作,而这三者是计算机最基本的工作单元,可以用来写系统软件。

7. C语言程序生成代码质量高,程序执行效率高

一般只比汇编程序生成的目标代码效率低10へ20%。

8. C语言适用范围大,可移植性好

C语言有一个突出的优点就是适合于多种操作系统, 如DOS、UNIX,也适用于多种机型。



C语言的缺点

1. C语言的缺点主要是表现在数据的封装性上,这一点使得C在数据的安全性上做的有很大缺陷,这也是C和C++的一大区别。

2. C语言的语法限制不太严格,对变量的类型约束不严格,影响程序的安全性,对数组下标越界不作检查等。从应用的角度,C语言比其他高级语言较难掌握。

[C语言指针]
指针就是C语言的一大特色,可以说C语言优于其它高级语言的一个重要原因就是因为它有指针操作可以直接进行靠近硬件的操作,但是C的指针操作也给它带来了很多不安全的因素。C++在这方面做了很好的改进,在保留了指针操作的同时又增强了安全性。Java取消了指针操作,提高了安全性。



C源程序的结构特点

1.一个C语言源程序可以由一个或多个源文件组成。

2.每个源文件可由一个或多个函数组成。

3.一个源程序不论由多少个文件组成,都有一个且只能有一个main函数,即主函数。

4.源程序中可以有预处理命令(include 命令仅为其中的一种),预处理命令通常应放在源文件或源程序的最前面。

5.每一个说明,每一个语句都必须以分号结尾。但预处理命令,函数头和花括号“}”之后不能加分号。

6.标识符,关键字之间必须至少加一个空格以示间隔。若已有明显的间隔符,也可不再加空格来间隔。




学习C语言


在初学C语言时,可能会遇到有些问题理解不透,或者表达方式与以往数学学习中不同(如运算符等),这就要求不气馁,不明白的地方多问多想,鼓足勇气进行学习,待学完后面的章节知识,前面的问题也就迎刃而解了,这一方面我感觉是我们同学最欠缺,大多学不好的就是因为一开始遇到困难就放弃,曾经和好多同学谈他的问题,回答是听不懂、不想听、放弃这样三个过程,我反问,这节课你听过课吗?回答又是没有,根本就没听过课,怎么说自己听不懂呢?相应的根本就没学习,又谈何学的好?
学习C语言始终要记住“曙光在前头”和“千金难买回头看”,“千金难买回头看”是学习知识的重要方法,就是说,学习后面的知识,不要忘了回头弄清遗留下的问题和加深理解前面的知识,这是我们学生最不易做到的,然而却又是最重要的。学习C语言就是要经过几个反复,才能前后贯穿,积累应该掌握的C知识。
那么,我们如何学好《C程序设计》呢?
一.学好C语言的运算符和运算顺序
这是学好《C程序设计》的基础,C语言的运算非常灵活,功能十分丰富,运算种类远多于其它程序设计语言。在表达式方面较其它程序语言更为简洁,如自加、自减、逗号运算和三目运算使表达式更为简单,但初学者往往会觉的这种表达式难读,关键原因就是对运算符和运算顺序理解不透不全。当多种不同运算组成一个运算表达式,即一个运算式中出现多种运算符时,运算的优先顺序和结合规则显得十分重要。在学习中,只要我们对此合理进行分类,找出它们与我们在数学中所学到运算之间的不同点之后,记住这些运算也就不困难了,有些运算符在理解后更会牢记心中,将来用起来得心应手,而有些可暂时放弃不记,等用到时再记不迟。
先要明确运算符按优先级不同分类,《C程序设计》运算符可分为15种优先级,从高到低,优先级为1 ~ 15,除第2、3级和第14级为从右至左结合外,其它都是从左至右结合,它决定同级运算符的运算顺序.

二.学好C语言的四种程序结构
(1)顺序结构
顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。
例如;a = 3,b = 5,现交换a,b的值,这个问题就好像交换两个杯子水,这当然要用到第三个杯子,假如第三个杯子是c,那么正确的程序为: c = a; a = b; b = c; 执行结果是a = 5,b = c = 3如果改变其顺序,写成:a = b; c = a; b = c; 则执行结果就变成a = b = c = 5,不能达到预期的目的,初学者最容易犯这种错误。 顺序结构可以独立使用构成一个简单的完整程序,常见的输入、计算,输出三步曲的程序就是顺序结构,例如计算圆的面积,其程序的语句顺序就是输入圆的半径r,计算s = 3.14159*r*r,输出圆的面积s。不过大多数情况下顺序结构都是作为程序的一部分,与其它结构一起构成一个复杂的程序,例如分支结构中的复合语句、循环结构中的循环体等。

(2) 分支结构
顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用分支结构。分支结构的执行是依据一定的条件选择执行路径,而不是严格按照语句出现的物理顺序。分支结构的程序设计方法的关键在于构造合适的分支条件和分析程序流程,根据不同的程序流程选择适当的分支语句。分支结构适合于带有逻辑或关系比较等条件判断的计算,设计这类程序时往往都要先绘制其程序流程图,然后根据程序流程写出源程序,这样做把程序设计分析与语言分开,使得问题简单化,易于理解。程序流程图是根据解题分析所绘制的程序执行流程图。
学习分支结构不要被分支嵌套所迷惑,只要正确绘制出流程图,弄清各分支所要执行的功能,嵌套结构也就不难了。嵌套只不过是分支中又包括分支语句而已,不是新知识,只要对双分支的理解清楚,分支嵌套是不难的。下面我介绍几种基本的分支结构。
①if(条件)
{
分支体
}
这种分支结构中的分支体可以是一条语句,此时“{ }”可以省略,也可以是多条语句即复合语句。它有两条分支路径可选,一是当条件为真,执行分支体,否则跳过分支体,这时分支体就不会执行。如:要计算x的绝对值,根据绝对值定义,我们知道,当x>=0时,其绝对值不变,而x<0时其绝对值是为x的反号,因此程序段为:if(x<0) x=-x;

②if(条件)
{分支1}
else
{分支2}
这是典型的分支结构,如果条件成立,执行分支1,否则执行分支2,分支1和分支2都可以是1条或若干条语句构成。如:求ax^2+bx+c=0的根
分析:因为当b^2-4ac>=0时,方程有两个实根,否则(b^2-4ac<0)有两个共轭复根。其程序段如下:
d=b*b-4*a*c;
if(d>=0)
{x1=(-b+sqrt(d))/2a;
x2=(-b-sqrt(d))/2a;
printf(“x1=%8.4f,x2=%8.4f\n”,x1,x2);
}
else
{r=-b/(2*a);
i =sqrt(-d)/(2*a);
printf(“x1=%8.4f+%8.4fi\n”r, i);
printf(“x2=%8.4f-%8.4fi\n”r,i)
}

③嵌套分支语句:其语句格式为:
if(条件1) {分支1};
else if(条件2) {分支2}
else if(条件3) {分支3}
……
else if(条件n) {分支n}
else {分支n+1}
嵌套分支语句虽可解决多个入口和出口的问题,但超过3重嵌套后,语句结构变得非常复杂,对于程序的阅读和理解都极为不便,建议嵌套在3重以内,超过3重可以用下面的语句。

④switch开关语句:该语句也是多分支选择语句,到底执行哪一块,取决于开关设置,也就是表达式的值与常量表达式相匹配的那一路,它不同if…else 语句,它的所有分支都是并列的,程序执行时,由第一分支开始查找,如果相匹配,执行其后的块,接着执行第2分支,第3分支……的块,直到遇到break语句;如果不匹配,查找下一个分支是否匹配。这个语句在应用时要特别注意开关条件的合理设置以及break语句的合理应用。

(3)循环结构:
循环结构可以减少源程序重复书写的工作量,用来描述重复执行某段算法的问题,这是程序设计中最能发挥计算机特长的程序结构,C语言中提供四种循环,即goto循环、while循环、do –while循环和for循环。四种循环可以用来处理同一问题,一般情况下它们可以互相代替换,但一般不提倡用goto循环,因为强制改变程序的顺序经常会给程序的运行带来不可预料的错误,在学习中我们主要学习while、do…while、for三种循环。常用的三种循环结构学习的重点在于弄清它们相同与不同之处,以便在不同场合下使用,这就要清楚三种循环的格式和执行顺序,将每种循环的流程图理解透彻后就会明白如何替换使用,如把while循环的例题,用for语句重新编写一个程序,这样能更好地理解它们的作用。特别要注意在循环体内应包含趋于结束的语句(即循环变量值的改变),否则就可能成了一个死循环,这是初学者的一个常见错误。
在学完这三个循环后,应明确它们的异同点:用while和do…while循环时,循环变量的初始化的操作应在循环体之前,而for循环一般在语句1中进行的;while 循环和for循环都是先判断表达式,后执行循环体,而do…while循环是先执行循环体后判断表达式,也就是说do…while的循环体最少被执行一次,而while 循环和for就可能一次都不执行。另外还要注意的是这三种循环都可以用break语句跳出循环,用continue语句结束本次循环,而goto语句与if构成的循环,是不能用break和 continue语句进行控制的。
顺序结构、分支结构和循环结构并不彼此孤立的,在循环中可以有分支、顺序结构,分支中也可以有循环、顺序结构,其实不管哪种结构,我们均可广义的把它们看成一个语句。在实际编程过程中常将这三种结构相互结合以实现各种算法,设计出相应程序,但是要编程的问题较大,编写出的程序就往往很长、结构重复多,造成可读性差,难以理解,解决这个问题的方法是将C程序设计成模块化结构。

(4)模块化程序结构
C语言的模块化程序结构用函数来实现,即将复杂的C程序分为若干模块,每个模块都编写成一个C函数,然后通过主函数调用函数及函数调用函数来实现一大型问题的C程序编写,因此常说:C程序=主函数+子函数。 因此,对函数的定义、调用、值的返回等中要尤其注重理解和应用,并通过上机调试加以巩固。

三.掌握一些简单的算法
编程其实一大部分工作就是分析问题,找到解决问题的方法,再以相应的编程语言写出代码。这就要求掌握算法,根据我们的《C程序设计》教学大纲中,只要求我们掌握一些简单的算法,在掌握这些基本算法后,要完成对问题的分析就容易了。如两个数的交换、三个数的比较、选择法排序和冒泡法排序,这就要求我们要清楚这些算法的内在含义
结语:当我们把握好上述几方面后,只要同学们能克服畏难、厌学、上课能专心听讲,做好练习与上机调试,其实C语言并不难学

C源程序的关键字---------------------------------------------------------------------------------------
所谓关键字就是已被C语言本身使用, 不能作其它用途使用的字。例如关键字不能用作变量名、函数名等
由ANSI标准定义的C语言关键字共32个 :
auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if while static
根据关键字的作用,可分其为数据类型关键字、控制语句关键字、存储类型关键字和其它关键字四类。
1 数据类型关键字(12个):
(1) char :声明字符型变量或函数
(2) double :声明双精度变量或函数
(3) enum :声明枚举类型
(4) float:声明浮点型变量或函数
(5) int: 声明整型变量或函数
(6) long :声明长整型变量或函数
(7) short :声明短整型变量或函数
(8) signed:声明有符号类型变量或函数
(9) struct:声明结构体变量或函数
(10) union:声明联合数据类型
(11) unsigned:声明无符号类型变量或函数
(12) void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
各种类型在内存中占的字节数有变化,如《c程序设计》第二版上说int 型占16比特数,但对于一个32位机来说,int型已经占32比特数了。
(2)控制语句关键字(12个):
A循环语句
(1) for:一种循环语句(可意会不可言传)
(2) do :循环语句的循环体
(3) while :循环语句的循环条件
(4) break:跳出当前循环
(5) continue:结束当前循环,开始下一轮循环
B条件语句
(1)if: 条件语句
(2)else :条件语句否定分支(与 if 连用)
(3)goto:无条件跳转语句
C开关语句
(1)switch :用于开关语句
(2)case:开关语句分支
(3)default:开关语句中的“其他”分支
D
return :子程序返回语句(可以带参数,也看不带参数)
3 存储类型关键字(4个)
(1)auto :声明自动变量 一般不使用
(2)extern:声明变量是在其他文件正声明(也可以看做是引用变量)
(3)register:声明积存器变量
(4)static :声明静态变量
4 其它关键字(4个):
(1)const :声明只读变量
(2)sizeof:计算数据类型长度
(3)typedef:用以给数据类型取别名(当然还有其他作用
(4)volatile:说明变量在程序执行中可被隐含地改变

计算机科学经典著作

1.The Art of Computer Programming

  Author: Donald.E.Knuth
  Web site: http://www-cs-faculty.stanford.edu/~knuth/taocp.html
  Book Info: 这部书被誉为20世纪最重要的20部着作之一,与Einstein的<<相对论>>并列,是计算机科学领域的权威着作.全书共分7卷,目前已经出版了3卷,被誉为"计算机程序设计理论的荷马史诗","可与牛顿的<<自然科学的数学原理>>媲美的巨着".作者数学方面的功底造就了本书严谨的风格,虽然本书不是用当今流行的程序设计语言描述的,但这丝毫不损伤它"程序设计史诗"的地位.道理很简单,它内涵的设计思想是永远不会过时的.The Art of Computer Programming 原计划要出七册,但目前只完成了三册.该书有日文,俄文,西班牙文等许多国的版本.其中,中文版由国防大学出版社发行.

  Book Reviews:
  无数的读者都在谈论Knuth的书所带来的深远影响.科学家惊叹于分析逻辑之透彻严谨,而普通的编程人员也已成功地将书中所列方案运用到他们的日常问题中.所有的人都非常赞赏Knuth在这套书中所表现的精确与风趣,并为其明确性与涉及面之广而感到欣喜.
  我无法向你表达这套书在学习和创造性方面所带给我的兴奋与激动,我已经将它们带入了我的生活,就像我的汽车,饭馆,工作,家庭……无所不在.
                             ----Charles Long
  无论你的背景怎样,如果你正在进行复杂的计算机编程,你就应该阅读本套书中的每本书,来补充你的专业知识.
  当一个问题难以解决,而必须使用Knuth的这套书来解决时,总是一件令人愉快的事情.我发现在计算机方面使用它们会有惊人的效果.
                             ----Jonathan Laventhol
  如果你认为你是一名真正优秀的程序员……读Knuth的<<计算机程序设计艺术>>,如果你能读懂整套书的话,请给我发一份你的简历.
                             ----Bill Gates
  The Art of Computer Programming, Volume 1: Fundamental Algorithms (3rd Edition)

  Author: Donald.E.Knuth
  Publisher: Prentice Hall
  Amazon Reviews: Amazon.com
  Book Info: 卷1为基础运算法则,该书以基本的编程概念和技术为开始,然后讲述信息结构--计算机内信息的表示法,数据元素间的结构关系以及处理它们的有效方法.主要应用于模拟,数字方法,符号计算,软件和系统设计.许多简单和重要的运算法则和技术已添加到前一版本中,精确的初步计算部分已经修改,以适应当前趋势.

  The Art of Computer Programming, Volume 2: Seminumerical Algorithms (3rd Edition)

  Author: Donald.E.Knuth
  Publisher: Prentice Hall
  Amazon Reviews: Amazon.com
  Book Info: 第2卷对半数值算法领域做了全面介绍,分"随机数"和"算术"两章.本卷总结了主要算法范例及这些算法的基本理论,广泛剖析了计算机程序设计与数值分析间的相互联系.第3版中特别值得注意的是Knuth对随机数生成程序的重新处理和对形式幂级数计算的讨论.

  The Art of Computer Programming, Volume 3: Sorting and Searching (2nd Edition)

  Author: Donald E.Knuth
  Publisher: Prentice Hall
  Amazon Reviews: Amazon.com
  Book Info: 卷3为分拣和搜索,这是本书的第1个修订版,它是对计算机分拣和搜索的一流技术的最全面的研究,它扩展了卷1中数据结构的处理方法,将大小数据库以及内存和外部存储都包含在内.本书包括对计算机方法仔细检查的选择方案,和其效率的大量分析.本书该版的独特之处在于优化了的分拣,以及对通用散列法和排列法的新的理论论述.

  作者简介:
  Donald.E.Knuth(唐纳德.E.克努特,中文名高德纳)是算法和程序设计技术的先驱者,是计算机排版系统TeX和METAFONT的发明者,他因这些成就和大量创造性的影响深远的着作(19部书和160篇论文)而誉满全球,在计算机科学领域享有崇高的威望,是计算机科学界公认的大宗师.作为斯坦福大学计算机程序设计艺术的荣誉退休教授,他当前正全神贯注于完成其关于计算机科学的史诗性的七卷集.这一伟大工程在1962年他还是加利福尼亚理工学院的研究生时就开始了.Knuth教授获得了许多奖项和荣誉,包括美国计算机协会图灵奖(ACM Turing Award),美国前总统卡特授予的科学金奖(Medal of Science),美国数学学会斯蒂尔奖(AMS Steele Prize),以及1996年11月由于发明先进技术荣获的极受尊重的京都奖(KyotoPrize).现与其妻Jill生活于斯坦福校园内.
  Donald.E.Knuth人生最辉煌的时刻在斯坦福大学计算机系渡过,获得了美国计算机协会图灵奖,成为本领域内当之无愧的泰斗.
----------------------------------------------------------------
2.Introduction to Algorithms

  Author:Thomas H.Cormen ,Charles E.Leiserson ,Ronald L.Rivest ,Clifford Stein
  Amazon Reviews: Amazon.com
  Book Info: 简称为CLRS的<<算法导论>>,被称作"计算机算法的圣经".
  本书的主要作者来自麻省理工大学计算机,作者之一Ronald L.Rivest 由于其在公开秘钥密码算法RSA上的贡献获得了图灵奖,目前是算法的标准教材,美国许多名校的计算机系都使用它,国内有些院校也将本书作为算法课程的教材.另外许多专业人员也经常引用它.由于TAOCP只出版了3卷,CLRS比较起前者来则显得内容更为全面,基本包含了所有的经典算法.本书程序全部由伪代码实现,这更增添了本书的通用性,使得利用各种程序设计语言的程序员都可以作为参考.语言方面通俗,很适合作为算法教材和自学算法之用.国内的很多作品名为数据结构,从本书中断章取义,把数据结构与算法混为一谈,搞得作者自己都迷迷糊糊.这也是我不十分愿意向大家推荐国内作品的原因.你会发现现在基本上所有的数据结构与算法书籍都会将本书作为参考文献之一,更可以说明一个问题,本书是作为读者进行算法学习的最佳选择.作为本书的补充内容,我愿意向大家推荐下面的学习资料:你可以通过这个地址找到本书的所有练习答案:http://www.itu.dk/people/beetle/ .为了更好的学习本书中的内容,最好的指导当然是来自作者本身讲述本书的课程,读者们可以通过http://18.89.1.101/sma/5503fall2001/index5503fall2001.html获得课程的录像.
----------------------------------------------------------------
3.Data Structure & Algorithm Analysis in C (Second Edition)

  Author:Mark Allen Weiss
  Published:September 1996
  Web site:http://www.cs.fiu.edu/~weiss/
  Amazon Reviews: Amazon.com
  Book Info: 本书曾被评为20世纪顶尖的30部计算机着作之一,作者Mark Allen Weiss在数据结构和算法分析方面卓有建树.他的数据结构和算法分析的着作尤其畅销,并受到广泛好评.已被世界500余所大学用作教材.
----------------------------------------------------------------
4.Concrete Mathematics A Foundation for Computer Science(Second Edition)

  Author:Donald.E.Knuth&Ronald L.Graham
  Web site: http://www-cs-faculty.stanford.edu/~knuth/gkp.html
  Amazon Reviews: Amazon.com
  Book Info: 很令人兴奋的就是这本书的主要作者正是泰斗Donald.E.Knuth以及他的同事Ronald L.Graham.书稿是他们在1970年的时候在计算机系教授研究生本门课程的基础上整理出来的教材.内容上是Knuth的巨着TAOCP第一部的扩展,有些比TAOCP中谈及地又深入了许多.重点放在高级计算机程序设计话题以及算法分析上,涉及了计算机科学领域内几乎所有可能遇到的数学知识.具体数学是离散数学和连续数学的综合,书中这点做得极为出色,介绍的内容涉及到书中许多经典问题的解答比目前广泛流传的解法更易懂.对于提高大家的计算数学修养有很大帮助.之所以TAOCP有的时候读者们读不懂就是因为计算数学基础不是十分扎实,但以我个人经验,若能对本书中内容有一个比较好的理解,会给你阅读如TAOCP这样的旷世巨着带来很大的方便.
----------------------------------------------------------------
5.Discrete Mathematics and Its Applications,Fourth Edition

  Author:Kenneth H.Rosen
  Amazon Reviews: Amazon.com
  Book Info: 本书的价值已经被全世界几百所大学所证实,作为离散数学领域的经典教材,全世界几乎所有知名的院校都曾经使用本书作为教材.以我个人观点看来,这本书可以称之为离散数学百科.书中不但介绍了离散数学的理论和方法,还有丰富的历史资料和相关学习网站资源.更为令人激动的便是这本书少有的将离散数学理论与应用结合得如此的好.你可以看到离散数学理论在逻辑电路,程序设计,商业和互联网等诸多领域的应用实例.本书的英文版(第五版)当中更增添了相当多的数学和计算机科学家的传记,是计算机科学历史不可多得的参考资料.作为教材这本书配有相当数量的练习.每一章后面还有一组课题,把学生已经学到的计算和离散数学的内容结合在一起进行训练.这本书也是我个人在学习离散数学时读的唯一的英文教材,实为一本值得推荐的好书.
----------------------------------------------------------------
6.The C Programming Language 2nd Edition

  Author:Brian W.Kernighan and Dennis M.Ritchie
  Publisher: Prentice Hall
  Published: 1988.
  Amazon Reviews: Amazon.com
  Book Info: Brian W.Kernighan和Dennis M.Ritchie的传世之作.千千万万程序员和编程爱好者的圣经,每一页都是价值连城的秘诀.不管你从哪一页信手翻起,最终你都会发现自己已经深陷其中而不能自拔.
----------------------------------------------------------------
7.The C++ Programming Language

  Author:Bjarne Stroustrap
  Amazon Reviews: Amazon.com
  Book Info: 由C++之父亲手执笔的<>是每个程序员心目中不可动摇的指南-----尽管官方标准还是ISO颁布的另一份文档,由ISO/IEC JTC1/SC22/WG21出版.TCPL是除了C++标准文献之外最权威的C++参考手册.和大多数人的看法不大一样,我认为Bjarne的文字语言并不逊色于他所创建的程序语言,至少我喜欢这种学院气息浓厚的作品.本书对C++语言的描述轮廓鲜明,直截了当.它从C++语言创建者的角度来观察C++,这是任何别的作者和书籍做不到的----没有任何人比Bjarne自己更清楚该怎么来使用C++.
  这是一本严肃的着作,以中,高级C++开发人员为目标读者.如果你是一名有经验的C++程序员,需要了解更加本质的C++知识,本书正是为你而写.它不是那种让你看了会不断窃喜的小书,需要用心体会,反复咀嚼.在阅读过程中,请特别留心Bjarne先生强调了什么,又对什么一语带过.我个人比较喜欢这本书的第四部分"使用C++做设计",这样的内容在类似的程序设计语言书籍中很难看到----我甚至认为Bjarne应该将这部分独立出来单独写一本书.
----------------------------------------------------------------
8.The Design and Evolution of C++

  Author: Bjarne Stroustrup
  Amazon Reviews: Amazon.com
  Book Info: D&E是一本关于C++语言设计原理,设计决策和设计哲学的专着.它清晰地回答了C++为什么会成为今天这个样子而没有变成另外一种语言.作为C++语言的创建者,Bjarne淋漓尽致地展示了他独到而深刻的见解.除了广受赞誉的语言特性外,Bjarne没有回避那些引起争议的甚至被拒绝的C++特性,他一一给出了逻辑严密,令人信服的解释.内容涵盖C++的史前时代,带类的C,C++的设计规则,标准化,库,内存管理,多重继承,模板等,对包括异常机制,运行时类型信息和名字空间在内的重要的新特性都分别进行了深入探讨.每一名C++程序员都应该可以从Bjarne的阐释中加深对手中这门语言的认识.这本书知识浓缩,信息量极大,请不要错过Bjarne每一句看似漫不经意的话.
----------------------------------------------------------------
9.Accelerated C++: Practical Programming by Example

  Author: Andrew Koenig, Barbara E.Moo
  Publisher: Addison-Wesley Professional
  Published: January 15, 2000
  Amazon Reviews: Amazon.com
  Book Info: 和市面上大多数C++教程不同,本书不是从"C++中的C"开始讲解,而是始于地道的C++特性.从一开始就使用标准库来写程序,随着讲述的逐渐深入,又一一解释这些标准库组件所依赖的基础概念.另外,和其他C++教材不同的是,这本书以实例拉动语言和标准库的讲解,对后两者的讲解是为了给实例程序提供支持,而不是像绝大多数C++教材那样,例子只是用作演示语言特性和标准库用法的辅助工具.
  作者在C++领域的编程实践,教育培训以及技术写作方面都是世界一流水准.我喜欢这种大量使用标准库和C++语言原生特性的清新的写作风格.在这本教材面前,几乎迄今为止的所有C++教材都黯然失色或显得过时.尽管这本教材也许对于国内的高校教育来说有些前卫,不过我仍然极力向我的同行们推荐.顺带一提,在Bjarne和我最近的一封通信里,他这样评价本书:对于有经验的程序员学习C++而言,这本书可能是世界上最好的一本.
----------------------------------------------------------------
10.C++ Gotchas: Avoiding Common Problems in Coding and Design

  Author: Stephen C.Dewhurst
  Publisher: Addison-Wesley Professional; 1st edition (November 26, 2002)
  Amazon Reviews: Amazon.com
  Book Info: Stephen的理论素养和实践经验注定这是一本值得一读的好书.Stephen曾经是贝尔实验室中第一批C++使用者.他已经使用C++成功解决了包括编译器,证券交易,电子商务以及嵌入式系统等领域中的问题.本书汇集了作者来自开发一线的99条编程真知灼见,洞悉它们,你可以避免几乎所有常见的C++设计和编程问题.
  我甚至认为,对于C++编程菜鸟而言,阅读这本书会比阅读Scott和Herb的书更能轻松而立竿见影地获得更大的提高.我个人很喜欢这本书的写作风格----Stephen的许多观点看似极端却无可辩驳.当然了,这种自信(以及冷幽默)来自于作者深厚的技术素养,而非自大的偏执.
----------------------------------------------------------------------------------
11.C++ Primer 3rd

  Author: Stanley B.Lippman
  Amazon Reviews: Amazon.com
  Book Info: 这本书的名字多少有点让人误解.尽管作者声称这本书是为C++新手而写,但无论是它的厚度还是讲解的深度都暴露了似乎并非如此.也许说它是一本"从入门到精通"的C++教程会更合适一些.我个人认为它并不适合完全不懂C++的初学者----在阅读这本书之前,你至少应该先有那么一点C或C++的背景知识,或者至少要具有一些其他语言的编程经验.
  尽管这本书省略了一些高级C++特性的讨论,但仍然可以称得上是迄今为止最全面的C++学习教程.事实上,如果一名C++初学者能够扎扎实实地读完本书并对照<>完成全部习题的话,他的水平肯定可以进入职业C++程序员的行列.我个人认为,即使你已经拥有了TCPL,这本书依然有拥有的价值,因为在许多方面它比TCPL来得更详细,更易懂.
----------------------------------------------------------------
12.Essential C++

  Author: Stanley B.Lippman
  Amazon Reviews: Amazon.com
  Book Info: 可以不太严格地认为这本书是<>的精简版.本书一一讲述了C++中最具代表性的主题,包括过程式编程,泛型编程,基于对象编程,面向对象编程,模板编程以及异常处理等.Stanley将门槛调低到"具有其他语言程序设计经验"的C++新手所能接受的最基本的层次,使他们能够迅速开始使用C++编程而又免于阅读<>那样的大部头.它以实例引导学习,力图使读者在最短的时间内把握C++的精粹.
  也许换一个人来概述C++编程范型(paradigm)的方方面面需要好几百页才能说清楚,但这本小书不可思议地做到了这一点.我个人非常喜欢这种满是技术,简明扼要并且"有话好好说"的书.这本书同样具有一个明显的风格:所有程序例子全部采用标准库组件,让人耳目一新.
----------------------------------------------------------------
13.Effective C++: 50 Specific Ways to Improve Your Programs and Design (2nd Edition)
  More Effective C++: 35 New Ways to Improve Your Programs and Designs


  Author: Scott Meyers
  Publisher: Addison-Wesley Professional; 2 edition (September 2, 1997)
  Amazon Reviews: Amazon.com
  Amazon.com
  Book Info: 先养成良好的C++设计习惯,这是Scott Meyers的<>和<>带给我们的无穷好处,这两本书是真正的经典,作者对C++的纯熟,使得语言的风格读起来简直是如饴甘甜,就像他站在你对面讲课.如果你已经深刻地理解了<>和<>,那你可以发现,你在众人中已经是鸡群之鹤.
  如果说<>主要讨论C++中一些相对基础的概念和技巧的话,那么<>则着重探讨了包括异常处理在内的一系列高级技术.与前者相比,后者具有两大主要区别:其一,它包含很多时新的标准C++的内容;第二,它讨论的主题倾向于"战略化"而非"战术化",并且讨论得更深入,更彻底.尤其是对虚析构函数,智能指针,引用计数以及代理类(proxy classe)等技术和模式论述的深入程度,让人很难想象是出现于这样的一本小书之中.
----------------------------------------------------------------
14.Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions
   More Exceptional C++: 40 New Engineering Puzzles, Programming Problems, and Solutions



  Author: Herb Sutter
  Book Info: 你自认为是一名C++语言专家吗?读一读ISO C++标准委员会秘书长的这两本书再回答.在这两本书中,Herb采用了"问答"的方式指导你学习C++语言特性.对于每一个专题,Herb首先合理地设想出你的疑问和困惑,接着又猜测出你十有八九是错误的解答,然后给你以指点并提出最佳解决方案,最后还归纳出解决类似问题的普适性原则.
  这两本书是典型的深究C++语言细节的着作,很薄,但内容密集,远远超过Scott的那两本书,读起来很费脑筋----我个人认为它们要比Scott的书难懂得多.若要研习这薄薄的两本书所包含的知识,至少需要花费数月的时间!(在Scott的荐序中,他坦陈不止一次陷入GotW问题的陷阱,你应该知道这意味着什么)对于语言细节的深究有什么好处呢?尽管在大多数情况下,我们不必关心C++代码幕后的动作,然而当我们不得不关心时,这两本书可以为我们提供很好的线索,因为它们揭示了C++语言中微妙而又至关重要的东西.
----------------------------------------------------------------
15.Modern C++ Design: Generic Programming and Design Patterns Applied

  Author: Andrei Alexandrescu
  Publisher: Prentice Hall
  Amazon Reviews: Amazon.com
  Book Info: 你自认为是C++模板编程高手吗?请看过这本书再回答.这是一本出自天才之手令人敬畏的杰作.泛型模式,无限延伸你的视野,足以挑战任何一名C++程序员的思维极限.
  这本书共分为两大部分,第一部分讨论了Loki程序库采用的基础技术以及一些高级语言特性,包括基于策略的类设计,模板局部特化,编译期断言,Typelist以及小型对象分配技术等.第二部分则着重介绍了Loki中的重要组件和泛型模式技术,包括泛化仿函数(Generalization Functor),单件(Singleton),智能指针,对象工厂(Object Factory),抽象工厂(Abstract Factory),访问者(Visitor)以及多方法(Multimethods)等.每一种技术都让人大开眼界,叹为观止.
----------------------------------------------------------------------------------
16.C++ Templates: The Complete Guide

  Author: David Vandevoorde, Nicolai M.Josuttis
  Amazon Reviews: Amazon.com
  Book Info: 有一种老套的赞美一本书的手法,大致是"没有看过这本书,你就怎么怎么地",这里面往往夸张的成分居多.不过,倘若说"没有看过<>,你就不可能精通C++模板编程",那么这个论断对于世界上绝大多数C++程序员来说是成立的.这本书填补了C++模板书籍领域由来已久的空白.此前,上有<>这样的专注于模板高级编程技术和泛型模式的着作,下有<>这样的针对特定模板框架和组件的使用指南.然而,假如对模板机制缺乏深入的理解,你就很难"上下"自如.鉴于此,我向每一位渴望透彻理解C++模板技术的朋友推荐这本书.
----------------------------------------------------------------
17.The C++ Standard Library: A Tutorial and Reference

  Author: Nicolai M. Josuttis
  Publisher: Addison-Wesley Professional; 1st edition (August 12, 1999)
  Amazon Reviews: [http://www.amazon.com/exec/obidos/tg/detail/-/0201379260/002-5778374-2660009?v=glance]Amazon.com[/URL]
  Book Info: 这是一本百科全书式的C++标准库着作,是一本需要一再查阅的参考大全.它在完备性,细致性以及精确性方面都是无与伦比的.本书详细介绍了每一标准库组件的规格和用法,内容涵盖包括流和本地化在内的整个标准库而不仅仅是STL.正如本书副标题所示,它首先适合作为教程阅读,尔后又可用作参考手册.
  浅显易懂的写作风格使得这本书非常易读.如果你希望学习标准库的用法并尽可能地发挥其潜能,那你必须拥有这本书.正如网络上所言,这本书不仅仅应该摆在你的书橱中,更应该放到你的电脑桌上.我向每一位职业C++程序员强烈推荐.
----------------------------------------------------------------
18.Effective C++: 50 Specific Ways to Improve Your Programs and Design (2nd Edition)


  Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library

  Author: Scott Meyers
  Publisher: Addison-Wesley Professional; 1st edition (June 6, 2001)
  Amazon Reviews: Amazon.com
  Book Info: 读完Scott 的<>和<>的中译本之后,我一直期待这本书的中文版.我从潘爱民先生的个人主页上了解到,他和他的合作伙伴似乎早已完成了这本书的翻译工作,可惜至今市面上仍不得见.幸运的是,我们可以看到它的原版.

本书是使用STL的程序员必读之作.在这本书中,Scott向我们讲述STL容器和算法的工作机制以及如何以最佳方式使用它们.和Scott的其他作品一样,这本书的写作风格清晰,精确,具有极佳的可读性.看过这本书以后,我想你也许会和我以及其他C++程序员一样产生这样的想法:Scott什么时候会写出一本"More Effective STL"?

----------------------------------------------------------------
19.Thinking in C++, Volume 1: Introduction to Standard C++ (2nd Edition)

  Author: Bruce Eckel
  Publisher: Prentice Hall; 2nd edition (April 15, 2000)
  Amazon Reviews: Amazon.com
  Book Info: <>的第1版于1996年荣获"软件研发"杂志评选的图书震撼大奖.最新推出的第2版对内容进行了大幅改写和调整,以反映C++标准化带来的影响以及近几年面向对象领域最新研究和实践成果."输入输入流","多重继承","异常处理"和"运行时类型识别"等高级主题连同C++标准化以后增加的一些内容则被放入第二卷中.Bruce是一名经验丰富的C++讲师和顾问,其培训和写作经验都是世界一流水准,他的作品比那些"玩票"的技术人员写的东西更能吸引读者.事实上,在同类图书中,对于大多数读者而言,这本书的可读性要超过TCPL和<>.

  Thinking in C++, Volume 2: Practical Programming (Second Edition)

  Author: Bruce Eckel
  Publisher: Prentice Hall; 2nd edition (November 1, 2003)
  Amazon Reviews: Amazon.com
  Book Info:
----------------------------------------------------------------
20.Ruminations on C++: A Decade of Programming Insight and Experience

  Author: Andrew Koenig, Barbara E. Moo, Barbara E. (Editor) Moo
  Publisher: Addison-Wesley Professional; 1st edition (August 7, 1996)
  Amazon Reviews: Amazon.com
  Book Info: Andrew是世界上屈指可数的C++专家.这是一本关于C++编程思想和程序设计技术而非语言细节的着作.如果你已经具有一定的基础,这本书将教你在进行C++编程时应该怎样思考,应该如何表达解决方案.整本书技术表达透彻,文字通俗易懂.Bjarne这样评价这本书:本书遍布"C++是什么,C++能够做什么"的真知灼见.
----------------------------------------------------------------
21.Design Patterns: Elements of Reusable Object-Oriented software

  Author: Erich Gamma
  Publisher: Addison Wesley Longman; (1998
  Amazon Reviews: Amazon.com
  Book Info: 设计可复用的面向对象的软件,你需要掌握设计模式.本书并非专为C++程序员而写,但它采用了C++(以及Smalltalk)作为主要示例语言,C++程序员尤其易于从中受益.四位作者都是国际公认的面向对象软件领域专家,他们将面向对象软件的设计经验作为设计模式详细记录下来.这本书影响是如此深远,以至于四位作者以及本书都被昵称为GoF(Gang of Four).本书学院气息浓厚,行文风格严谨简洁,虽然它不如某些讲解模式的书籍易读,但真正要精准地理解设计模式,本书是终极权威.学习设计模式,这本书需要一而再,再而三的咀嚼.顺带一句:请将设计模式化作开拓思维的钥匙,切莫成为封闭思维的枷锁.
----------------------------------------------------------------
22.Efficient C++: Performance Programming Techniques

  Author: Dov Bulka, David Mayhew
  Publisher: Prentice Hall
  Amazon Reviews: Amazon.com
  Book Info: 这本超薄小书聚焦于高性能C++应用程序开发.两位作者都是IBM软件专家,都工作于对性能要求极高的系统构建领域,本书是他们的经验之谈.也有人不喜欢这本书,因为它花了不少的篇幅讲述和C++无关的东西,我却恰恰因为这一点而对这本书产生好感,正是这些东西让我开阔了眼界.
----------------------------------------------------------------------------------
23.Inside the C++ Object Model

  Author: Stanley B.Lippman
  Publisher: Addison-Wesley Pub Co
  Published: May 3, 1996
  Amazon Reviews: Amazon.com
  Book Info: 从编译器的角度观察C++可以使你知其然并知其所以然.本书探讨了大量的C++面向对象程序设计的底层运作机制,包括构造函数,函数,临时对象,继承,虚拟,模板的实例化,异常处理,运行期类型识别等,另外还介绍了一些在实现C++对象模型过程中做出的权衡折衷.喜欢刨根问底的C++程序员可以从中了解了无数的编译器解释源代码的细节,以及记忆体分配的细节..
----------------------------------------------------------------------------------
24.The Art of Assembly Language

  Author: Randall Hyde
  Amazon Reviews: Amazon.com
  Book Info: 本书以X86系列微机为背景,从简单的Hello程序开始,系统而详细地阐述了X86微机汇编语言编程的各种基础知识和编程技巧,内容涉及到数据表示,存储器管理,各种数据类型,过程,与汇编语言相关的体系结构,控制结构,文件,宏指令,位处理指令,字符串指令,MMX指令,类和对象,以及混合语言编程等,尤其是在高级汇编语言(HLA)方面,该书给予了细致深入的讲解.对于有意学习X86汇编语言编程的程序员来说,这是一本难得的好书.
  本书的作者Randall Hyde在大学中教授汇编语言十多年,并且开发了好几个商用软件,具有丰富的汇编语言开发经验.该书的英文电子版受到了成千上万的网站和高级程序员的高度评价,已经成为了高级汇编语言编程方面的一本经典之作.虽然该书的出版时间不长,但在Amazon上为该书作出评价的人很多,而且几乎所有的人都给予5星的评价,可见其内容之好.可以相信,随着时间的推移,它在程序员中的影响会越来越大.
----------------------------------------------------------------
25.The Design and Implementation of the 4.4BSD Operating System

  Auther:
  Marshall Kirk McKusick, Consultant
  Keith Bostic, Berkeley Software Design, Inc.
  Michael J.Karels, Berkeley Software Design, Inc.
  John S.Quarterman, Texas Internet Consulting
  Publisher: Addison-Wesley Publishing Company
  Published: 04/30/1996
  Book Info: 是第一本完整描述伯克利最新版本4.4BSD设计和实现的权威性着作.书中介绍了4.4BSD的内部结构和实现4.4BSD的系统功能中所用的概念,数据结构和算法.书中着重对4.4BSD 和 AT&T System V UNIX 版本的不同处作了较详细的描述,并对其设计思想及背景作了清晰的阐述.该书对研究,开发和使用 UNIX 系统,特别是4.4BSD UNIX 实现中的一些新技术,新特点.有很大的参考价值.
----------------------------------------------------------------
26.John Lions:Lions' Commentary on UNIX 6th Edition with Source Code

  Author: John Lions
  Amazon Reviews: Amazon.com
  Book Info: 在20世纪70年代和80年代这一时期,莱昂的几册<<源代码分析>>,曾是人们争相传看的UNIX系统的"地下出版物".我们今天来审视这一着作,尽管其中的代码已属过时,但其注释者所作的大部分注释,依然完美如初.要想学习操作系统原理,就必须阅读和理解源代码.莱昂的工作,使我们中间的大多数人都能达到这一步.
  Book Reviews:
  本书中的材料确定是有点过时了,其内容不包括图形,网络以及1975年后出现的新事物.即使在1979年的编译中也不再使用的线性搜索,基本数据结构和C代码还保留在本书的源代码中,而该代码面向的计算机非常简单,只比存储器稍稍多一些.从中你会发现有很多粗糙之处.但是在代码中你也可以观察到其基本结构,该结构沿用了很长时间,而且能够包容在计算环境中发生的巨大变化.在莱昂的分析中,你可以觉察到新鲜的,经常提出问题的立场,其中的很多词语和思想都很适宜于教育和学习.莱昂非常清楚地赞赏他阅读的UNIX源代码,但又立即指出其不足之处.他帮助他的学生理解蕴含在源代码中的很多思想和主题,也坦率地说明他对该代码某些部分感到的困惑之处.此处重印的文献已经教育了一代人,它是计算机界中复印数量最多的一本书稿.将此公开记录在案是件大好事.
                             ----丹尼斯.M .里奇
  最后我还想提及的是:现在,得到最广泛传播的一份地下计算机科学文献已经可以自由地使用了.我对1977年中的那一天仍然是记忆犹新,那时我接到邮寄来的此书的第一份草稿,开始时我对此书并无很多期望,但是最后却是逐字逐句进行了仔细阅读.20年之后,此书依旧是对一个实际操作系统工作的最好分析说明.
                             ----肯·汤姆森
  (汤姆森和里奇因为开发和实现UNIX操作系统而共同获得1983年ACM美国计算机协会图灵奖).
----------------------------------------------------------------
27.Operating Systems: Design and Implementation (Second Edition)

  Author: Andrew S.Tanenbaum, Albert S.Woodhull
  Publisher: Prentice Hall; 2nd edition (January 15, 1997)
  Amazon Reviews: Amazon.com
  Book Info: 本书是一本理论结合实践的杰出教材.作者不仅介绍了所有基本原理,如:进程,进程间通信,输入/输出,虚拟存储,文件系统及系统安全,也介绍了线程概念,同时又详尽地讨论了MINIX系统(一种UNIX兼容系统),使学生不仅能学到原理,还能通过使用MINIX来了解如何将这些原理应用到真实的操作系统中.作者更新了所有原理性材料以反映该领域的新进展.基于POSIX的MINIX系统经修订后可运行在基于奔腾(Pentium)的计算机上.MINIX代码可用于Ethernet和TCP/IP联网环境.每本书均附光盘,内含MINIX全部源码和可运行各种计算机上的两个模拟程序.每章后均附思考题.
----------------------------------------------------------------
28.Modern Operating Systems (2nd Edition)

  Author: Andrew Tanenbaum
  Publisher: Prentice Hall
  Amazon Reviews: Amazon.com
  Book Info: 对于软件开发专家以及计算机专业的学生来说,<<现代操作系统>>给出了操作系统设计一个概念上的全面见解,包括Unix/Linux和Windows 2000的详细个案研究.本书共分两部分,第一部分详尽讲述了传统操作系统知识,包括进程,存储器管理,文件系统,I/O设备管理,死锁等内容;第二部分主要介绍了分布式操作系统,包括层次协议,远程过程调用,互斥操作,分布式文件系统等专题.为加深概念的理解,本书还详细介绍了四个操作系统,包括两个传统的系统UNIX和MS-DOS;两个分布式系统Amoeba和Mach.此外还简要介绍了NFS,AFS,ISIS等其他几个系统.本书体系完整,内容丰富,叙述清晰,是大学计算机及相关专业学生不可多得的教科书,对于从事计算机管理,开发,系统分析等职业的专业人员也是优秀的参考书.
----------------------------------------------------------------
29.Operating System Concepts

  Author: Abraham Silberschatz, Greg Gagne, Peter Baer Galvin
  Amazon Reviews: Amazon.com
  Book Info: 本书是计算机类专业操作系统课程的一本经典教材,自第一版问世以来,经历了近20年的锤炼,被认为是该课程教材的一本"圣经".它对操作系统的概念和基本原理给出了清晰的阐述.本书所涉及的基本概念和算法均基于当前商用操作系统,并在非特定操作系统的通用环境中展开讲解.书中介绍了大量与流行操作系统相关的实现技术,包括Solaris2,Linux,Windows NT,Windows 2000,OS/2和Apple Macintosh操作系统.此版包括了线程,Windows 2000的新章节,并新增了客户/服务器
模型和网络文件系统,嵌入式操作系统,实时操作系统,分布式操作系统等.
----------------------------------------------------------------
30.Running MS-DOS

  Author: Van Wolverton
  Published: 18 November 1993
  Book Info: MS-DOS 上的清晰工作!
  经过10年无数次竞争,<>仍是被其它 MS-DOS 奉为标准的书.Van Wolverton 的实际的写作风格,上百张屏幕演示,使得<>成为任何时候的DOS指南畅销书.再没有哪个计算机书从读者和评论家那里获得过这么多的赞扬.
  这本新的,10 周年纪念版本,包括关于 MS-DOS 6.2 最新版本的重要的信息.如果您仅仅只是买了一台 PC 机,或者只是升级到了一个新版本,<>应当成为您的第一个信息源.<>实际上是由3本书合成的,包括:
  MS-DOS 介绍,以及如何同您的计算机硬件相协调
  一个关于了解和使用 MS-DOS 的完整制导
  一个命令手册,里面有定义,例子和表格,帮您快速寻找信息!
----------------------------------------------------------------
31.Advanced Programming in the UNIX® Environment

  Author: Richard W.Stevens, Stephen A.Rago
  Publisher: Addison-Wesley Professional
  Amazon Reviews: Amazon.com
  Book Info: 与大多数其他操作系统类似,Unix对运行其上的程序提供了大量服务,此外,本书与其他Unix编程书籍不同的是,本书除了讲述Unix系统的编程接口----即系统调用接口和由标准C函数库提供的大量函数之外,还通过许多例子和基本原理对Unix编程环境做了更深入的解释和剖析,如怎样创建数据库,页面描述语言的打印机驱动程序,现代拨号程序以及代理进程,处理机间通信,伪终端程序等.本书作者是Unix研究领域的专家,而本书更被誉为是该领域的圣经宝典,相信每一位读者都会在学习本书的过程中体会到它的价值所在.
----------------------------------------------------------------
32.Unix Network Programming
 
  Unix Network Programming, Volume.1: The Sockets Networking API, Third Edition
  Author: W.Richard Stevens, Bill Fenner, Andrew M.Rudoff, Richard W.Stevens
  Publisher: Addison-Wesley Professional
  Amazon Reviews: Amazon.com
  Book Info: 这是一本利用应用编程接口(API)编写法对计算机网络进行通信的网络编程的教程.所用的API有两类: 基于BerkeleyUNIX操作系统的"Berkeley"套接字和基于AT&T开发的X Open传输接口XTI.本书分四大部分:Ⅰ.引论和TCP/IP;Ⅱ.基本套接字(Sockets);Ⅲ.高级套接字;Ⅳ.XTI X/Open传输接口.四个附录: A.IPv4,IPv6,ICMPv4和ICMPv6;B.虚拟网络;C.调试技术;D.一些关键源代码.全书内容全面,实用性强,是一本极好的参考书.

  UNIX Network Programming, Volume 2: Interprocess Communications (2nd Edition)
  Author: W.Richard Stevens
  Publisher: Addison-Wesley Professional
  Amazon Reviews: Amazon.com
  Book Info: 一种良好的进程间通信(IPC)形式是复杂精致的UNIX程序性能之关键.本书全面深入地介绍了各种进程间通信形式,包括消息传递,同步,共享内存及远程调用(RPC).作者从介绍IPC的基础及其所要解决的问题开始,逐步引导读者学习如何使System V IPC和新的Posix标准两者都达到极至.书中Pthread的内容有较大的扩充,所附的许多例子反映多个线程而不是多个进程.读者通过本书将能掌握各种现行的IPC技术,其中有:管道和FIFO;Posix和System V消息队列;互斥锁和条件变量;读写锁;记录上锁;Posix和System V信号灯;Posix和System V共享内存;Solaris门和Sun RPC;IPC技术的性能测量.本书内容详尽且具有权威性,几乎每章都有精选的习题,是计算机和网络专业高年级本科生和研究生的首选教材,本书也可作为网络研究和开发人员的自学教材和参考书.
----------------------------------------------------------------
33.TCP/IP Illustrated
 

  Web site: http://www.kohala.com/start/

  TCP/IP Illustrated,Volume 1:The Protocols
  Author: W.Richard Stevens
  Publisher: Addison-Wesley
  Amazon Reviews: Amazon.com
  Book Info: <>是一本完整而详细的TCP/IP协议指南.描述了属于每一层的各个协议以及它们如何在不同操作系统中运行.作者用Lawrence Berkeley实验室的tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输的不同分组.对tcpdump输出的研究可以帮助理解不同协议如何工作.本书适合作为计算机专业学生学习网络的教材和教师参考书.也适用于研究网络的技术人员.

  TCP/IP Illustrated,Volume 2:The Implementation
  Author: Gary R.Wright, W.Richard Stevens
  Publisher: Addison-Wesley
  Amazon Reviews: Amazon.com
  Book Info: 本书完整而详细地介绍了TCP/IP协议是如何实现的.书中给出了约500个图例,15,000行实际操作的C代码,采用举例教学的方法帮助你掌握TCP/IP实现.本书不仅说明了插口API和协议族的关系以及主机实现与路由器实现的差别.还介绍了4.4BSD-Lite版的新的特点,如多播,长肥管道支持,窗口缩放,时间戳选项以及其他主题等等.读者阅读本书时,应当具备卷1中阐述的关于TCP/IP的基本知识.本书适用于希望理解TCP/TP协议如何实现的人,包括编写网络应用程序的程序员以及利用TCP/IP维护计算机网络的系统管理员.

  TCP/IP Illstrated,Volume 3:TCP for Transactions,HTTP,NNTP,and the UNIX Domain Protocols
  Author: W.Richard Stevens
  Publisher: Addison-Wesley
  Amazon Reviews: Amazon.com
  Book Info: 本书是<>的延续.主要内容包括:TCP事务协议,即T/TCP,这是对TCP的扩展,使客户-服务器事务更快,更高效和更可靠;TCP/IP应用,主要是HTTP和NNTP;UNIX域协议,这些协议提供了进程之间通信的一种手段.当客户与服务器进程在同一台主机上时,UNIX域协议通常要比TCP/IP快一倍.本书同样采用了大量的实例和实现细节,并参考引用了卷2中的大量源程序.本书适用于希望理解TCP/IP如何工作的人,包括编写网络应用程序的程序员以及利用TCP/IP维护计算机网络的系统管理员.

  作者简介:
  Gary R.Wright 研究TCP/IP多年.他是Connix公司的董事长,这个公司的基地在康涅狄格州,它提供Internet接入和咨询服务.W.Richard Stevens(1951-1999)是一位非常受人尊敬的专家,除了<>三卷本外,他还有其他两部最为畅销的作品;<>和<>(两卷本).
----------------------------------------------------------------
34.Routing TCP/IP
 

  CCIE Professional Development:Routing TCP/IP Volume 1
  Author: Jeff Doyle
  Publisher: Cisco Press; 1st edition (July 1, 1998)
  Amazon Reviews: Amazon.com
  Book Info: 本书是第一本详细而又完整地介绍互联网络内部网关路由选择协议(IGRP)的专业书籍,堪称有关IGRP方面不可多得的经典之作.本书共分三个部分.第一部分主要介绍了网络和路由选择的基本知识,对TCP/IP和静态,动态路由选择技术作了一个整体的回顾.第二部分是本书的精华,这一部分详细深入地讲述了各种常用的内部网关路由选择协议,如静态路由,RIP,RIPv2,IGRP,EIGRP,OSPF,ISIS等,每一章除了对该协议的实现机制和参数详尽阐述,使读者对协议的实现原理有一个清晰的理解外,还通过在实际网络环境中的实例,详细地论述了该协议在Cisco路由器上的配置和故障处理方法,使读者获取大量解决实际问题的专业技能.第三部分介绍了如缺省路由,路由过滤等多种有效的路由控制工具,用来创建和管理多个IP路由选择协议的协调工作.本书不仅适合那些需要准备通过CCIE考试的考生,而且也适合任何需要完整理解TCP/IP内部路由选择协议的网络设计和工程人员阅读.本书中对协议细节的讲解和对网络实例的探讨相信会让读者获益匪浅.

  CCIE Professional Development Routing TCP/IP Volume 2
  Author: Jeff Doyle, Jennifer DeHaven Carroll
  Publisher: Cisco Press; 1st edition (April 11, 2001)
  Amazon Reviews: Amazon.com
  Book Info: 本书深入系统地阐述了TCP/IP路由技术,内容包括几种重要的网络协议,如外部网关协议(EGP),边界网关协议(BGP4),以及相应的高级IP路由技术与应用――网络地址转换,IP组播路由技术,IPv6技术,路由器管理等.本书共分9个章节,首先介绍并讨论了主题内容的基本原理,接着是设计用来展示真实网络环境中相关概念的一系列配置范例,最后提供给读者经实践验证过的故障排除方法以解决网络可能会出现的问题.重点介绍了自治系统之间的路由选择策略以及诸如组播和IPv6等更具挑战性的路由选择和实施技术.
----------------------------------------------------------------
35.Data and Computer Communications,Seventh Edition

  Author: William Stallings
  Amazon Reviews: Amazon.com
  Book Info: 本书是着名计算机专业作家 William Stallings 的经典着作之一,内容涉及最基本的数据通信原理,各种类型的计算机网络及多种网络协议和应用.这一版对原有内容做了彻底的修订和重组,使新版对通信各专题的阐述更全面,更清晰.同时,新版加强了无线通信和组网,吉比特以太网,区分服务,MPLS和TCP/IP实现细节等内容.此外,本书还包括词汇表,参考文献,缩写词对照表.每章都附有习题和建议,以便读者进一步阅读.
----------------------------------------------------------------
36.Programming Pearls, 2nd Edition

  Author: Jon Bentley
  Publisher: Addison-Wesley Professional; 2 edition (September 27, 1999)
  Amazon Reviews: Amazon.com
  Boo

计算机科学经典论文

从Jao的Programming Musing 看到的:Babar Kazar 整理了一堆经典论文。Jao强烈建议每个严肃的程序员读每篇论文,说它们都或多或少有意思。粗粗扫了一下,很多论文都没读过。挑了些俺多少知道一点的介绍。


· An axiomatic basis for computer programming C. A. R. Hoare
Tony Hoare名下的公理化语义(Axiomatic Semantics)。著名的Hoare Triples, P{C}Q, 就是从这里来的。论文不长,双列6页。前辈们就是这样的,6页纸就能开宗立派。不像俺,6页纸连介绍部分都写不周全。哪位老大想知道怎么证明程序正确。前置条件,不变条件,后置条件的妙用,可以用这篇论文开牙。
· Communicating Sequential Processes (CSP) C. A. R. Hoare
Hoare, 又见Hoare。其实也正常。牛人之牛,就在于成就深广。链接的文档应该不算论文,而算专著。260页。从1985年推出到现在20多年过去,这本书的引用率在CS历史上排名第三,可见其影响之深。对并发编程有强烈兴趣的老大可以去钻研一把。我没读过。
· Call-by-name, call-by-value, and the lambda calculus Gordon Plotkin
没读过。只见LtU介绍过。Gordon老大这篇论文的要点之一是要想顺利地对程序进行推导,就需要有合适的lambda理论。想深入理解call-by-name,call-by-value,和lambda算子的老大们可以上了。
· Towards a theory of type structure John C. Reynolds
号称经典中的经典。不过也没读过。类型系统一直是编程语言研发的热点,也是非常有趣的方向――类型系统的编程好比让机器证明一系列定理。Reynolds在论文里讨论了什么才是正确的类型结构,和句法正确必须独立于任何具体的类型表达形式,并且给出了带类型的lambda算子的一种扩展,允许他描述用户自定义类型和多态函数。满篇公式,有勇气去读的老大要有心理准备。
· Structured Programming with go to Statements Donald E. Knuth
这篇论文详细结构化编程时讨论了什么时候用goto,什么时候不用goto。高爷爷精细务实的态度非常值得学习。高老太爷用了一辈子goto(MIX和MMIX程序里没了Goto怎么玩儿得转嗫?),岂能轻易被Dijkstra对goto的批评吓退?他仔细探讨了几种不同的程序,考察goto用在那些程序里的利弊。最后得出结论,goto在某些程序里仍然高效实用。虽然论文是30年前的,但里面的分析手法和利用goto的优化技术至今可用。
· Definitional interpreters for higher-order programming languages John C. Reynolds
这篇文章俺喜欢。”Metacircular”这个性感的概念就是在这篇论文里首次提出的。想深入了解用一门语言写出的解释器定义这门语言自身的神奇理念,这篇论文是必读材料。有兴趣的老大可以先读SICP的第四章。
· An APL Machine 1970 Philip S. Abrams
只知道APL是门有历史意义的语言。顺便说一句,APL这个名字太土了。A Programming Language ==APL。象什么话嘛。


· The Anatomy of a Large-Scale Hypertextual Web Search Engine Sergey Brin and Lawrence Page
网络是个大的矩阵(transition probability matrix of Markov Chain)。网页的声誉(page rank)就是这个巨大矩阵的principle eigenvector的某个元素。嗯,反正我只有佩服的份儿。
· No Silver Bullet: Essence and Accidents of Software Engineering Frederic P. Brooks, Jr.
地球银都知道。不用俺多嘴了。
· A Mathematical Theory of Communication Claude Shannon
Bell实验室当年辉煌一时。出了名的叫人做A,结果发明了B。香农老大就是其中杰出代表。香农进了Bell实验室后,居然没人吩咐他干嘛。香农老大转念一想,自己喜欢数学,Bell的生意尽在通讯,干嘛不看看把数学应用到通讯上有什么结果呢?于是1948年这篇论文问世乐。搞通讯的人崩溃乐。现代信息理论就诞生乐。
· Bayesian Networks without Tears
贝叶斯理论热了好几年了。估计还会继续热下去。现在信息越来越多,我们已经审美疲劳。大家渴望的不是信息,而是知识。靠个人的力量把信息提炼成知识太慢,我们需要机器的帮忙。机器学习不热都难,而贝叶斯理论在机器学习里有很好的应用。这篇文章行为浅显,可以轻松读完。对了,那个人人喝骂的微软回形针的智能引擎就是用贝叶斯网络实现的。
· A Universal Algorithm for Sequential Data Compression
没读过。无耻地找个借口:我们系开信息理论课的时候,俺刚好毕业。
· A Relational Model of Data for Large Shared Data Banks 1970 Edgar F. Codd
没有关系代数,人类将会怎样?Codd划时代的论文奠定了现代数据库的基础。嘿嘿,其实俺也没有读过这篇论文。顺便说一句,现在的ORM试图把data schema和对象系统映射起来。问题是,data schema只是对关系的一种表达方式而已,还和具体的系统实现有关。也许把对象间的结构和关系映射起来才是正道。
· Let's Build a Compiler 1988-1995
教你一步一步写出一坨编译器。不算论文吧。一篇相当不错的指南。
· Gauging Similarity via N-Grams: Language-Independent Sorting... Marc Damashek
第一次听说
· Worse Is Better Richard P. Gabriel
网上脍炙人口的文章。很有教育意义。简单说,worse is better包括下面几点:
-- 简单:设计要简单。但如果接口和实现不能两全,追求实现的简单。文章里给出的Unix vs Multics的例子非常有意思。
-- 正确:程序必须在所有可见的方面正确。其它地方,如果简单和正确不能两全,追求简单。
-- 一致性:程序不能太不一致。但为了简单,可以在少数地方不一致。
-- 完备性:程序应该尽可能照顾到重要的地方,但是不能牺牲简洁。
强烈推荐。
· Hints on Programming Language Design C.A.R. Hoare
Hoare对设计语言的经验总结。这些经验至今有效。文章很容易读,读后绝对增长程序设计的功力。
· Why Functional Programming Matters John Hughes
为普通程序员准备的大餐,所以写得通俗。没有公式,也没有拗口的术语。着重展示了Fold和Map的强大抽象能力。不由想到我在大学里修的一门课,编程语言。课是好课,老师是一流老师。课上我们学习了浅显的程序语言理论,重点学习了函数编程(用Common Lisp)和逻辑编程(用Prolog)。这门课彻底改变我对编程的理解,明白了imperative programming和OO programming外还有精彩世界。至今想来都觉得幸运。那门课的作业也很有意思,实现一个驻留内存的数据库,支持关系代数里的常见操作。
· On the Expressive Power of Programming Languages Matthias Felleisen
没读过。待读。
· The Early History Of Smalltalk Alan Kay
还有什么好说的呢?Alan Kay这个名字说明一切。30年前Alan Kay就做出来Smalltalk,现在想来仍然让人惊叹。引一段文章Alan Kay评述Smalltalk的话:In computer terms, Smalltalk is a recursion on the notion of computer itself. Instead of dividing "computer stuff" into things each less strong than the whole--like data structures, procedures, and functions which are the usual paraphernalia of programming languages--each Smalltalk object is a recursion on the entire possibilities of the computer. Thus its semantics are a bit like having thousands and thousands of computer all hooked together by a very fast network. Questions of concrete representation can thus be postponed almost indefinitely because we are mainly concerned that the computers behave appropriately, and are interested in particular strategies only if the results are off or come back too slowly.
· Computer Programming as an Art Donald E. Knuth
高老太爷在1974年图灵奖仪式上的致词。真是顶尖geek的风范啊。高太爷在文章里解释了问什么他的书取名为《编程的艺术》。明显他对人们谈到编程时把科学置于艺术之上很不了然。高爷爷追溯“艺术”的词源,说艺术的本意就是技能,也是技术和技巧两次的起源。从这里开始,他开始讨论艺术和科学的关联,讨论艺术在编程里的表现形式和意义。用他的话说,他作为教育者和作者的毕生目标就是叫人写美妙的程序。读起来让人心潮彭湃的说。
· The next 700 programming languages Peter J. Landin
42年前的论文,影响深远。Peter在论文里描述的函数语言ISWIM(If You See What I Mean)现在没有几个人知道了。但他对lambda算子的推崇和对函数语言的论述影响了后来的函数语言设计。
· Recursive Functions of Symbolic Expressions and their Computation by Machine (Part I) 1960 John McCarthy
47年前提出LISP的那篇著名论文。没读过。动态类型检查,Garbage Collection, 递归函数,S-expression, 程序及数据。。。可谓贡献辉煌。


· FORTH - A Language for Interactive Computing Charles H.Moore
只知道Forth是一门stack oriented的编程语言,影响了后来的一些语言,比如CAT。其它的就不知道了。
· Teach Yourself Programming in Ten Years 2001 Peter Norvig
大牛之所以为大牛,原因之一就是目光深远。这篇文章批评那些《24秒学会C++》之类教材的无稽,讨论了学习编程,从菜鸟变成鲲鹏的方法。中文版已经传得满世界都是,赶快找来看吧。Peter Norvig的网站上还有很多高质量的文章。强烈推荐一读。
· The Definition and Implementation of a Computer Language based on constraints Guy Lewis Steele Jr.
好像是Guy Steels的硕士论文。没读过。
· Growing a Language Guy Lewis Steele Jr.
好文!G老大在OOPSLA 98上的主题演讲。G老大主张应该采取渐进的方式设计一门可以被自由扩展的语言(LISP圈子里的牛人们多半都持这种观点吧?)。这篇演讲稿针对该观点做了精练地论述。说起进化的观点,可以参看另外一篇好文章,SICP作者之一,Jay Sussman的近作。
· Epigrams on Programming Alan J. Perlis
A老大发表的一系列关于编程的格言。幽默而深刻。每读必笑。笑后必哭。嗯嗯嗯,夸张一下。不要当真。
· The Complexity of Theorem Proving Procedures Stephen A. Cook
仙风道骨的库克爷爷的成名作。这篇文章一出,好比有人在加州荒漠里发现第一块狗头金,立刻掀起开发加州的狂潮。计算复杂性理论迅速遍地开花。相比这篇论文开创性的贡献,库克因此得到图灵奖不过小小点缀。NP-Complete在这篇论文里被严格定义。更重要的是,库克证明了第一个NP-Complete的问题,SAT(Boolean Satisfiability Problem)。有了SAT,再加上折磨了无数学生的Polynomial Reducibility,无数的NPC问题就出现乐。。。别看俺在这里唾沫横飞,当年做有关计算理论的证明题还是相当吃力的,没有少熬夜。奇怪的是,某一天我给同学讲解我的解法,NPC的相关定义突然变得清晰起来。当初让我绞尽脑汁的证明竟然变得相当机械。后来知道,给人讲解(包括写作)是非常有效地学习方法。怀着备课的目标读文章,假设自己给别人讲解正在读的文章,有助快速理解所读内容。SAT的证明相当复杂,我反正没有耐心读完。
· Steps Toward Artificial Intelligence Marvin Minsky
AI的奠基论文。不过我没读过。
· The Original 'Lambda Papers' Guy Steele and Gerald Sussman
一系列讲解lambda算子和scheme设计的经典论文。学scheme时读过,对理解scheme的设计理念很有帮助。


· The UNIX Time-Sharing System Dennis Ritchie and Ken Thompson
作者不用介绍了吧?这篇文章里介绍的Unix特性早为人熟知。不过第八部分(VIII Perspective)讨论了作者的设计理念,仍然值得一读。

2008年4月27日星期日

UNIX V7 I/O 系统

本文给出对 I/O 系统的是如何工作的概括描述。它着眼于为驱动程序作者提供指导,并且更加面向于描述设备驱动程序的环境和本质,而不是处理普通文件的文件系统的那部分实现。

假定读者对在“UNIX 分时系统”一文中讨论的整体结构有良好的了解。在“UNIX 实现”中有更详细的讨论,本文重申了其中的这一部分,但更加详细一些。本文基本上就是源代码的一份注释,所以最适合与系统代码联合使用。

January, 1979


--------------------------------------------------------------------------------

设备类别

有两类设备: 块设备和字符设备。块接口适用于如磁盘、磁带、和 DECtape 这样的设备,它们工作或可以工作在 512 字节块的寻址方式下。把普通的磁带勉强归入此类的原因是:尽管只能在磁带的末尾处写块,但通过使用前向和后向间隔(space:越过几个文件或记录)可以读任意的块。块设备潜在的至少包含一个可挂装的文件系统。块设备的接口是高度结构化的;这些设备的驱动程序共享一个缓冲池和大量例程。

字符类型设备有更加直接的接口,但更多的工作必须由驱动程序自己来做。

两种类型的设备都用一个主和一个次设备号来命名。这些编号通常存储为一个整数,次设备号在低端 8 位中而主设备号在邻近的高 8 位中;可利用宏 major 和 minor 来访问这些编号。主设备号选择哪个驱动程序处理这个设备;次设备号不由系统的其余部分使用而是在适当的时候传递给驱动程序。典型的次设备号选择连结到一个给定控制器上的一个子设备,或者是类似的一些硬件接口。

块和字符设备把主设备号作为在各自独立的表中的索引,它们都开始于 0 故此重叠。

I/O 概述

open 和 creat 系统调用的目的是在三个独立的系统表中设置条目。其中第一个是 u_ofile 表,它存储在系统的每进程一个的数据区 u 中。 这个表用 open 或 creat 返回的文件描述符作为索引,并在 read、write、或在打开的文件上的其他操作期间被访问。其中的条目只包含到 file 表的相应条目的一个指针,file 表是每系统一个的数据库。每个 open 或 creat 的实例在 file 表中都有一个条目。这个表每系统一个的原因是,在打开文件之后 fork 出来的多个进程之间共享打开文件的同一个实例。一个 file 表条目包含一个标志,它指示打开文件是用来读或是用来读写还是用作一个管道的;和一个计数,用来决定何时使用这个条目的所有进程都已经终止或关闭了这个文件(这样就可以放弃这个条目)。还有一个 32 位文件偏移量,用来指示在文件中何处发生下一次读写。最后,还有到 inode 表中的条目的一个指针,inode 表包含这个文件的 i-node 的一个复件。

可以把特定的打开文件指派为“复用”文件,对这样的通道可以应用许多其他标志。在这种情况下,不再使用偏移量,转而使用到相关的多路通道表的一个指针。本文不讨论多路通道。

在 file 表中的一个条目严格的对应着 open 或 creat 的一个实例;如果同一个文件已经被多次打开,则它在这个表中有多个条目。但是,对于一个给定的文件在 inode 表中最多只有一个条目。还有,一个文件进入 inode 表的原因不止是被打开了,可能的原因还有:它是一些进程的当前目录,或者它是包含一个当前挂装的文件系统的一个特殊文件。

在 inode 表中的一个条目与存储在磁盘上相应的 i-node 有些区别,对表中的条目扩充了一个标志字,包含关于这个条目的信息,一个计数,用来决定何时允许它消失,和这个条目对应的 i-node 的设备及 i 编号。还有,一些块编号给出文件的寻址信息,这个文件是从磁盘上使用的 3 字节压缩格式展开成完全的长数量的。

在处理对特殊文件的 open 或 creat 调用期间,为了顾及所要求的特殊处理(比如回绕一个磁带,开启调制解调器的数据中断就绪线路,等等),系统总是调用这个设备的 open 例程。但是,只在最后一个进程关闭一个文件,也就是在收回这个 i-node 表条目的时候才调用 close 例程。因此让设备维护或依赖一个它自己的用户计数是不可行的,但实现在关闭之前不能被重新打开的一个独占设备是完全可能的。

当 read 或 write 发生时,使用这个用户实际参数和这个 file 文件表条目来设置变量 u.u_base、 u.u_count、和 u.u_offset,它们分别包含目标区域的(用户)地址,要传送的字节计数,和在文件中的当前位置。如果这个文件引用一个字符类型的特殊文件,则调用适当的读或写例程;如下面讨论的那样,它负责正确的传送数据并更改计数和当前位置。否则,使用当前位置来计算在这个文件的一个逻辑块编号。如果这个文件是一个普通文件,则必须把这个逻辑块号(可能使用了间接块)映射到一个物理块编号;而一个块设备特殊文件不需要映射。这个映射由 bmap 例程完成。如下面讨论的那样,在所有情况下都使用结果的物理块编号来读或写适当的设备。

字符设备驱动程序 

cdevsw 表指定字符设备提供的接口例程。每个设备提供 5 个例程: open、close、read、write、和特殊功能(用来实现 ioctl 系统调用)。可以缺少其中任何一个。如果应该忽略在这个例程上的一个调用,(比如,在不需要设置的一个非独占设备上的 open) cdevsw 条目可以给出为 nulldev; 如果它应该被考虑为一个错误,(比如在一个只读设备上写)则使用 nodev。对于终端,cdevsw 结构总是包含到与这个终端相关联的 tty 结构的一个指针。

在每次打开文件的时候,以完整的设备编号作为参数来调用 open 例程。第二个参数是一个标志,只在要在这个设备上写的时候它才是非零。

只在最后一次关闭文件的的时候调用 close 例程,此时在其中打开了这个文件的最后一个进程关闭了这个文件。这意味着驱动程序不可能维护它的用户的自己的计数。第一个参数是设备编号;第二个参数是一个标志,如果在进行最终的 close 的进程中为了写而打开了这个文件,则这个标志是非零。

在调用 write 的时候,提供设备编号作为参数。设置每用户变量 u.u_count 为由用户指出的字符的数目;对于字符设备,这个数最初可以是 0。u.u_base 是用户提供的地址,以它为开始接受字符。系统可以内部调用这个例程,所以提供了用做指示的标志 u.u_segflg,如果是 on,则 u.u_base 引用系统地址空间而不是用户地址空间。

write 例程将从这个用户的缓冲区向这个设备复制直到 u.u_count 个字符,为每个传送的字符减少 u.u_count,write 例程一次操作一个字符,使用例程 cpass( ) 从用户的缓冲区获取字符。连续的调用它来返回要写的字符,直到 u.u_count 变成 0 或发生了一个错误,发生错误时它返回 -1。cpass 负责查询(takes care of interrogating) u.u_segflg 并更新 u.u_count。

Write 例程还可以使用例程 iomove(buffer, offset, count, flag) 来把可能大量的字符传送到一个内部缓冲区,在必须移动许多字符的时候会更快。iomove 把直到 count 个字符传送到 buffer 的从这个缓冲区的开始处偏移 offset 字节的地方;在写的情况下 flag 应当是 B_WRITE (就是 0)。警告: 调用者负责确保这个计数不过长并且是非零。出于效率而做一个提示,如果 buffer+offset、count 或 u.u_base 中的任何一个是奇数,则 iomove 会非常慢。

调用设备的 read 例程的情况类似于 write,但是要保证 u.u_count 是非零。要向用户返回字符,可使用例程 passc(c);它担负与 cpass 类似的工作,并且对由 u.u_count 指定的最后一个字符向用户返回 -1;此前返回 0。对于 write 也可以使用 iomove;标志应当是 B_READ,但提出的警告相同。

“特殊功能”例程由 stty 和 gtty 系统调用以如下方式来调用: (*p) (dev, v),这里的 p 是到这个设备的例程的一个指针,dev 是设备编号,而 v 是一个向量。在 gtty 的情况下,假定这个设备把最多 3 个字的状态信息放置到这个向量中;把它返回给调用者。在 stty 的情况下,v 是 0;设备将从数组 u.u_arg[0...2] 取得最多 3 个字的控制信息。

最后,每个设备都应该有适当的中断时间(interrupt-time)例程。在中断发生的时候,进入在这个设备的中断例程上的一个 C 相容调用。中断捕获机制制作中断的陷入向量中的“新 PS”字的低端四位,中断处理程序将获得它。处理多个类似的设备的驱动程序可以方便的用它来编码从设备号。在中断处理已经被处理了之后,把中断处理程序的返回作为中断自身的返回。

字符设备驱动程序可以获得一些有用的例程。例如,多数这种处理程序需要在内部接口中有一个地方用来在它们的“顶半部分”(读/写)和“底半部分”(中断)例程之间缓冲字符。对于相对低数据率的设备,最好的机制是 getc 和 putc 例程维护的字符队列。队列头部有如下结构

struct { int c_cc; /* 字符记数 */ char *c_cf; /* 第一个字符 */ char *c_cl; /* 最后一个字符 */ } queue;
通过 putc(c, &queue) 把一个字符一个字符放置在队列的尾部,这里的 c 是一个字符而 queue 是这个队列的头部。如果没有空间放置这个字符则例程返回 -1,否则返回 0。通过 getc(&queue) 可以取回这个队列上的第一个字符,它返回(非负的)字符,或者在队列为空时返回 -1。

注意给队列中字符的空间由系统中所有的设备共享,并且在标准系统中只能获得 600 个字符槽。所以设备处理器,特别是写例程,必须小心避免吞下超额数目的字符。

对于设备处理程序可获得的其他主要帮助是睡眠-唤醒机制。调用 sleep(event, priority) 导致进程等待(允许其他进程运行)直到事件发生;此时,把进程标记为准备好运行,并在没有更高优先级的进程的时候返回。

调用 wakeup(event) 指示事件已经发生了,就是说,导致在这个事件上睡眠的进程被唤醒。事件是由睡眠者和唤醒者达成协议的一个任意数量。为了方便,它是驱动程序使用某个数据区的地址,其守侯的事件是唯一的。

在一个事件上睡眠的进程不应该假定这个事件已经发生;它应该检查导致它们睡眠的条件是否不再保持着。

优先级的范围是 0 到 127;越大的数值指示越少优惠的调度状况。在睡眠在小于参数 PZERO 的优先级的进程和数值上更大的优先级的进程之间有区别。前者尽管有可能被交换出去,但不能被信号所中断。所以在可能永远不发生的一个事件上以小于 PZERO 的优先级睡眠是个坏主意。在另一方面,如果进程同时被机器中的某个信号所终止,则大优先级的到 sleep 的调用可能会永不返回。顺便提一下,在中断时间调用的例程中调用 sleep 是不可原谅的错误,因为正在运行的进程必定不是应该去睡眠的进程。同样的,中断例程不应该触及用户数据区“u”中的变量,更不用说变动了。

如果一个设备驱动程序想要等待的事件是非常规的或不可能提供一个 wakeup,(例如,一个在线设备,它通常不导致一个中断),则可以给出 sleep(&lbolt, priority) 调用。lbolt 是一个外部单元,时钟中断例程每 4 秒钟唤醒它的地址一次。

可获得例程 spl4()、spl5()、spl6()、spl7() 把处理器设置为指定的特权级别,以此避免来自设备的不方便处理的中断。

如果一个设备需要知道实时间隔,可以使用 timeout(func, arg, interval)。这个例程安排在 interval 个 60 分之一秒之后, 将用 arg 作为参数调用 func,函数样式是 (*func)(arg)。使用超时函数的例子有,在打字机输出中的功能字符象换行和 tab 之后提供实时延迟,如果在一个指定的秒数之内没有响应则终止读 201 Dataphone dp 的尝试。注意 60 分之一秒的数目被限制为 32767,因为它必须出现为正数,并且一次只能进行有限数目的 timeout。还有指定的 func 在时钟中断时调用,所以它必须符合中断例程的一般要求。

块设备接口

块设备的处理由管理一组缓冲区的一系列例程作为中介,缓冲区包含在各种设备上的数据块的映像。这些例程的最重要的功能是保证:访问相同设备相同块的、多道程序方式下的多个进程、对在这个块中数据保持一致的视觉(view)。次要但仍很重要的作用是通过对经常访问的块保持块的内存复件来增加系统的效率。这个机制的主要数据库是缓冲区表 buf。每个缓冲区头部包含一对指针(b_forw, b_back),它们维持与特定块设备相关联的缓冲区的一个双向链表,和另一对指针(av_forw, av_back),它们通常维持“空闲”块的一个双向链表,就是说,这些块适合于重新分配给其他事务。有 I/O 在进行中或忙于其他目的的缓冲区不在这个列表中出现。这个缓冲区头部还包含这个缓冲区所参照的设备号和块号,和到与这个缓冲区相关联的实际存储的一个指针。有一个字记数,它是要传送到缓冲区或从缓冲区传送出的字的数目的一个负数;还有一个错误字节和一个剩余字记数,把它们用做从一个 I/O 例程到它的调用者的通信信息。最后,有一个标志字,它的位指示这个缓冲区的状态。后面将讨论这些标志。

七个例程构成与系统的余下部分之间的接口的最重要的部分。给出一个设备号和块号,bread 和 getblk 二者返回到这个块的缓冲区头部的一个指针;区别是 bread 确保返回的缓冲区实际上包含这个块的当前数据的,而 getblk 返回的缓冲区只在这个块已经在内存中的情况下包含这个块的数据(是否在内存中由后面讨论的 B_DONE 位来指示)。在二者中任意情况下,这个缓冲区和相应的设备块都被标记为“忙”,所以引用这个块的其他进程被迫等待直到它变成空闲。例如,在一个块要被完全重写的时候使用 getblk,故此它以前的内容不再有用;仍然不允许其他进程引用这个块直到把新数据放置到其中。

使用 breada 例程来实现预先读。它在逻辑上类似于 bread,但是接受作为一个补充参数的(在同一个设备上的)一个块的编号,在指定要求的块可获得之后要异步读取它。

给出到一个缓冲区的一个指针,brelse 例程使这个缓冲区对其他进程再次可获得。例如,在使用 bread 提取了数据之后调用它。有三个有细微差别的写例程,它们都接受一个缓冲区指针作为参数,并且它们都通过把缓冲区放置到空闲列表上来在逻辑上释放它被其他用户使用。bwrite 在正确的设备队列上发放置缓冲区,等待写完成,如果需要的话设置用户的错误标志。bawrite(异步写)把缓冲区放置在设备队列上,但是不等待完成,所以错误不能直接反应给用户。bdwrite(延迟写)根本不启动任何 I/O 操作,而是仅标记这个缓冲区,这样如果碰巧从空闲列表上获取了它用于持有来自其他块的数据,则首先写出在它内部的数据。

当你想要确保 I/O 正确的进行的时候使用 bwrite,并且把错误反映给适当的用户;例如,在更新 i-node 的时候使用它。bawrite 在想要更多重叠的时候有用(因为不要求等待 I/O 完成),但要在有理由确定真正需要这次写的时候。在不能确定这次写立刻就需要的时候使用 bdwrite。例如,当 write 系统调用的最后的字节达不到块的尽头的时候调用 bdwrite,这假定将要给出重新使用同一个块的另一次写。在另一方面, 在达到了一个块的尽头的时候调用 bawrite,因为这个块可能不会被马上再次访问,并且你也希望尽可能的快的开始写处理。

在任何情况下,注意例程 getblk 和 bread 确定给定块对调用者是独占式使用的,并使其他人等待,直到最后调用 brelse、bwrite、bawrite、或 bdwrite 中的一个来释放这个块来由别人使用。

如上所述,每个缓冲区头部包含一个标志字,它指示这个缓冲区的状态。因为它们在驱动程序和块 I/O 系统之间提供了信息的一个重要通道,理解这些标志是非常重要的。下面的名字是列出的常量,它们选择相关的标志位。

B_READ :当把缓冲区交给这个设备的策略例程(见后)的时候设置此位来指示读操作。符号 B_WRITE 被定义为 0,而且不定义标志;提供它作为对例程的调用者的一种助记方便,比如 swap 例程有一个独立的参数指示读或写。
B_DONE :当把缓冲区交给这个设备的策略例程的时候设置此位为 0,并且在这个操作完成的时候开启此位,通常也作为发生错误时的结果。还使用它作为 getblk 的返回参数的一部分,来指示若是 1 则返回的缓冲区实际上包含了在要求的块中的数据。
B_ERROR :当设置了 B_DONE 来指示发生了一个 I/O 或其他错误的时候设置此位为 1。如果设置了此位,缓冲区头部的 b_error 字节若是非零则包含一个错误代码。如果 b_error 是 0 则未指定这个错误的性质。实际上目前还没有驱动程序设置 b_error;提供后者以备将来改进,籍此实现更详细的错误报告方案。
B_BUSY:此位指示缓冲区头部不在空闲列表上,就是说,指示某人独占使用。这个缓冲区仍被连接到与它的设备相关联的块列表上。当 getblk (或者是调用它的 bread) 查找给定设备的缓冲区列表找到要求的块并在其上发现设置了此位,则睡眠直到此位清除。
B_PHYS:在 PDP 11/70 上需要分配 Unibus 映射的原始 I/O 事务上设置此位。
B_MAP:在分配了 Unibus 映射的缓冲区上设置此位,这样 iodone 例程就知道释放这个映射了。
B_WANTED:这个标志与 B_BUSY 位联合使用。在如上所述睡眠之前,getblk 设置这个标志。 反过来,当释放这个块并且 busy 为关闭(在 brelse 中)的时候,如果 B_WANTED 开启着则对这个块的头部给予一个 wakeup。这个策略避免了每当释放一个缓冲区的时候由于有其他人可能需要它而必须调用 wakeup 所带来的花费。
B_AGE:在释放缓冲区之前在缓冲区上设置此位;如果它是开启的,把这个缓冲区放置到空闲列表的开头部分,而不是结束部分。当调用者判定不会马上再次使用同一块的时候可以使用这个启发性的行动。
B_ASYNC:此位由 bawrite 设置来向适当的驱动程序指示当写完成的时候应当释放这个缓冲区,这通常在中断时间。bwrite 与 bawrite 之间的区别是前者启动 I/O,等待直到完成,并释放这个缓冲区。后者只是设置此位并启动 I/O。此位指示在完成的时候应当对这个缓冲区调用 relse。
B_DELWRI:此位由 bdwrite 在释放这个缓冲区之前设置。当 getblk 在查找空闲块期间,发现在一个缓冲区中此位为 1,它会另外获取,这导致这个块在重新使用之前要被写出。
块设备驱动程序

bdevsw 表持有接口例程的名字和给每个块设备的一个表的名字。

如同对字符设备那样,块设备驱动程序可以为每次打开和最后关闭这个设备分别提供一个 open 和一个 close 例程。不用分开的读和写例程,每个块设备驱动程序都有一个策略(strategy)例程,调用它要把到缓冲区头部的一个指针作为参数。如上所讨论的那样,缓冲区头部包含一个读/写标志,内存地址,块编号,一个(负数)字计数,和主从设备号。策略例程的角色是依据在缓冲区头部中的信息的要求完成操作。当事务完成的时候应当设置 B_DONE (可能还有 B_ERROR)位。接着如果设置了 B_ASYNC 位,则应当调用 brelse;否则调用 wakeup。在设备有能力在免错操作下传送比要求的更少的字的情况下,这个设备的字计数寄存器应当被放置到这个缓冲区头部的剩余计数槽中;否则,剩余计数应当被设置为 0。这个特殊机制实际上对磁带驱动程序有用处;当读这个设备时记录小于要求是很正常的,并且用户应当被告之记录的实际长度。

尽管给策略例程的最普通的参数是按上述讨论分配的一个真实的缓冲区头部,实际上需要的参数是到包含正确信息的地方的一个指针。例如管理内核映象的移动进出交换设备的 swap 例程,使用这个设备的策略例程。小心在这个标志字中没有无关的位被开启了。

设备的表由 bdevsw 指定,它的组成包括包含一个活跃标志的一个字节和一个错误计数,一对连接,它们构成这个设备的缓冲区链表的头部(b_forw, b_back),和设备队列的首端和末端指针。在这些东西中,除了缓冲区链表指针之外,都只被设备驱动程序自身使用。典型的这个标志编码这个设备的状态,最小的用处是指示这个设备当前忙于传送信息并且不应发起新的命令。错误计数用于在错误发生时统计重试的数目。设备队列用来记住入栈的请求,在最简单的情况下它可以维持为一个先入先出列表。因为已经被交给策略例程的缓冲区不在空闲缓冲区列表上,在缓冲区中的维持空闲列表的指针(av_forw, av_back)也被用来持有维持这个设备队列的指针。

提供了一组对块设备驱动程序有用的例程。iodone(bp) 安排把 bp 指向的缓冲区在适当的时候释放和唤醒,这时策略模块已经完成了这个缓冲区,要么正常要么有错误。(在后一种情况大概会设置 B_ERROR 位。)

可以使用例程 geterror(bp) 来检查在缓冲区头部中的错误位,并安排把在那里发现的任何错误提示反映给用户。它只能在驱动程序的非中断部分调用,这时 I/O 已经完成(B_DONE 已经设置)。

原始块设备 I/O

设立了一种方案让块设备驱动程序可以提供在用户内存映象和设备之间的直接传送信息的能力,而不需要使用缓冲区并且块的大小同调用者需要的一样大。这种方法包括设置对应于这个原始设备的一个字符类型的特殊文件,并提供 read 和 write 例程,它们用正确的信息设置专有的、非共享的缓冲区头部并调用这个设备的策略例程。如果需要的话,可以提供单独的 open 和 close 例程,但通常不需要。一个特殊功能例程应当唾手可得,特别是对于磁带。

需要做大量的工作生成“正确的信息”来放置到这个策略模块的参数缓冲区中;最差的部分是把重新部署了的用户地址映射到物理地址。多数这种工作由 physio(strat, bp, dev, rw) 完成,它的参数是策略例程的名字 strat、缓冲区指针 bp、设备号 dev、和读-写标志 rw、它的值是 B_READ 或 B_WRITE。physio 确保这个用户的基地址和计数是偶数(因为多数设备工作以字为单位)并且影响的内存区域在物理空间上是连续的;它延迟直到缓冲区不忙,并在操作进行中是它为忙;还有它设置用户错误返回信息。

UNIX V7 实现

摘要

本文用高层术语描述常驻的 UNIX 内核的实现。这些讨论分为三部分。第一部分描述 UNIX 系统如何看待进程、用户、和程序。第二部分描述 I/O 系统。最后一部分描述 UNIX 文件系统。

January, 1979


--------------------------------------------------------------------------------

1. 介绍

UNIX 内核由大约 10,000 行 C 代码和大约 1,000 行汇编代码组成。这些汇编代码可进一步分割成 200 行是出于效能的缘故而被包含的(它们可以用 C 来写)和 800 行被用来完成不能用 C 写的硬件功能。

在组建成广义的“UNIX 操作系统”的所有代码中的这些内核代码占了 5 到 10 个百分点。内核是唯一不能依据用户的喜好来替换的 UNIX 代码。因此,内核应当尽可能少的做实际决定。这不意味着允许用户做同一件事情有无数种选择。而是意味着做一件事情只允许一种途径,但让这个途径成为可能已经提供了的所有选择(option)的最小公约数。

在内核中实现什么或不实现什么,体现了重大的责任和强大的力量二者。它是遵循“做事情所应有的途径”的一个“肥皂盒”平台。即使如此,如果“这个途径”太根本(radical)了,则没有人会依从它。每个重要决定都被谨慎的权衡了。从始至终,高效性替代了简单性。只在可局部化其复杂性的条件下使用复杂的算法。

2. 进程控制

在 UNIX 系统中,用户在叫做用户进程的环境中执行程序。在要求一个系统功能的时候,用户进程把系统作为一个子例程来调用。在这个调用中的某一点上,有一个明显的环境切换。此后,这个进程被称为系统进程。在进程的正规定义中,用户进程和系统进程是同一个进程的不同阶段(它们从不同时执行)。出于保护,每个系统进程都有一个自己的栈。

用户进程执行自一个只读的正文段,它可以被所有执行同一个代码的所有进程所共享。共享正文段没有带来功能上的利益。不需要把只读段交换出去的事实带来了效能上的利益,因为在次级存储中的最初的复件(copy)仍然存在。这对在等待终端输入期间要被交换出去的交互程序有很大的好处。进而,如果两个进程同时执行自一个只读段的相同的复件,则只有一个复件需要在主存中驻留。由于一个程序的同时执行不是常见的,所以这个效果是次要的。作为一个讽刺,这个效果节省了主存的使用,但只在主存过于充足(overabundance)的时候发挥作用,此时有足够的内存来保持处于等待中的进程被装载着。

在系统中所有的当前只读正文段用正文表来维护。正文表条目持有这个正文段在次级存储中的位置。如果这个段被装载了,则这个表还持有主存位置和共享这个条目的进程的记数。当这个记数减少到零的时候,这个条目与持有这个段的主存和次级存储一起释放。当一个进程首次执行一个共享正文段的时候,分配一个正文条目并把这个段装载到次级存储上。如果第二个进程执行了已经分配了的一个正文段,简单的增加这个条目的引用记数。

用户进程在它自己的数据段中包含一些完全私有的读写数据。系统尽可能的不使用用户的数据段来持有系统数据。特别是,在用户地址空间中没有 I/O 缓冲区。

用户数据段有两个增长边界。一个边界由系统作为内存故障(fault)的结果而自动增加,它用于栈。第二个边界只能用显式的请求来增长(或缩小)。新近分配的主存的内容被初始化为零。

还有一个固定大小的系统数据段与进程有关联并一起被交换。这个段包含只在进程活跃的时候系统才需要的关于这个进程的所有数据。系统数据段中的包含的各种数据的例子有: 保存的中央处理器寄存器,打开的文件描述符,记帐信息,临时(scratch)数据区,和给这个进程的系统阶段的栈。系统数据段从用户进程是不可寻址的并以此来保护。

最后,有给每个进程一个条目的进程表。这个条目包含当这个进程不活跃的时候系统需要的所有数据。例如,进程的名字,其他段的位置,和调度信息。在建立进程的时候分配进程表条目,并在进程终止的时候释放。这个进程条目总是可以被内核直接寻址的。

图 1 展示了各种进程控制数据之间的联系。某种意义上,进程表是所有进程的定义(definition),因为从进程表条目出发可以访问与一个进程有关的所有数据。



2.1. 进程建立和程序执行

通过系统基本操作 fork 来建立进程。新建立的(子)进程是原始(父)进程的复件。在这两个进程之间没有可检测到的主存共享。(当然,如果父进程执行自一个只读正文段,则子进程将共享这个正文段。) 为子进程做出所有可写数据段的复本。在 fork 之前打开的文件在 fork 之后真正的被共享。把它们在这种关系中的位置通知进程,允许它们选择它们自己的(通常是不一致的)命运。父进程可以 wait (等待)任何它的子进程的终止。

进程可以 exec (执行)一个文件。这包括为这个文件中指定的新文本和数据段对换当前正文和数据。丢弃旧段。做 exec 不改变进程; 做 exec 的进程继续存在,但在 exec 之后它执行了一个不同的程序。在 exec 之前打开的文件在 exec 之后仍然打开。

如果一个程序,假定这个程序是一个编译器的第一遍,想要用另一个程序覆盖自己,假定那个程序是第二遍,则它可以简单的 exec 第二个程序。这是“跳转”的同义语。如果一个程序想要在 exec 另一个程序之后保持控制,则它应该 fork 一个子进程,让子进程 exec 这个程序,并让父进程 wait 子进程。这是“调用”的同义语。把调用分解成一个绑定和随后的一个转换类似于 SL-5 中的子例程连接。

2.2. 交换

需要时,把与一个进程有关的主要数据(用户数据段,系统数据段,和正文段)与次级存储来回交换。在连续的主存中保存用户数据段和系统数据段来减少交换延迟。(如果使用了低延迟设备,如 bubble、CCD、或 scatter/gather 设备,必须重新考虑这个决定)。进行主存和次级存储二者的分配使用相同的首次适配(first-fit)算法。当一个进程增大的时候,分配一片新的主存。把旧内存中的内容复制到新内存中。释放旧内存并更改有关的表。如果没有足够的主存,则分配次级存储来替代。把这个进程交换到次级存储上,准备好以新的大小交换进主存。

交换进程是内核中一个独立的进程,它简单的把其他进程交换进出主存。它检查进程表找到已被交换出去并准备好了运行的一个进程。它为这个进程分配主存并把它的段读取到主存中,这个进程将在主存中与其他载入的进程竞争中央处理器。如果没有可获得的主存,交换进程通过检查进程表找到可以交换出去的进程来得到可使用的主存。它选择一个进程来交换出去,把它写到次级存储,释放主存,并回过头来查找要交换进来的进程。

这样交换进程就有两个特定算法。把交换出去的多个进程中的哪个交换进来? 这由次级存储驻留时间决定。 被交换出去时间最长的最先交换进去。对大进程有轻微的处罚。把装载的多个进程中的哪个交换出去? 首先挑出等待慢事件(比如,非当前运行或等待磁盘 I/O)的进程,用在主存中的年龄(age)来挑选,再次带有大小处罚。(如果没有的话)用相同的年龄算法检查其他进程,除非它们到了至少为规定的年龄,否则不取出它们。这为交换增加了滞后(hysteresis)并防止整体抖动(thrashing)。

这些交换算法是系统中最另人疑惑的。对于有限的主存,这些算法导致整体交换。这本身并不坏,因为交换不冲击驻留进程的执行。但是,如果交换设备还必须用于文件存储,交换流量严重的冲击文件系统流量。想要双重利用有限的磁盘资源的小系统确实如此。

2.3. 同步和调度

通过使进程等待事件来完成进程同步。事件用任意整数来表示。出于方便,事件被选择为与这些事件相关的表的地址。例如,等待某个子进程终止的一个进程将等待一个事件,这个事件是它自己的进程表条目的地址。 在进程终止的时候,它通告(signal)用它的父进程的进程表条目表示的这个事件。通告没有进程在等待的一个事件没有任何效果。类似的,通告有许多进程在等待的一个事件将把他们都唤醒。这与 Dijkstra 的 P 与 V 同步操作有相当的区别,这里没有与事件相关的记忆。所以不需要事件的分配先于它们的使用。 事件仅仅通过被使用而存在。

在相反的方面,因为没有有事件相关联的记忆,通过事件机制不能通告“有多少”的概念。例如,想要内存的进程可能等待有内存分配有关的一个事件。当任何数量的内存变得可获得的时候,将通告这个事件。所有竞争的进程都将被唤醒来争夺这个新内存。(实际上,交换进程是唯一的等待主存变成可获得的进程。)

如果在一个进程决定等待一个事件的时刻和这个进程进入等待状态的时刻之间这个事件发生了,则这个进程将等待已经发生(并可能永不再次发生)的一个事件。这种竞争条件偶尔出现的原因是没有与这个事件相关联的记忆来指示这个事件已经发生了;一个事件的唯一的动作是把一组进程从等待状态变更为运行状态。进程切换只能通过对事件等待机制的显式的调用而在内核中发生,这个事实在相当大程度上解除了这个问题。如果了正在提及的这个事件由其他进程来通告,则没有问题。但是如果这个事件由一个硬件中断来通告,则必须进行特别关照。在改变 UNIX 以适应多处理器配置的时候,这种同步竞争是暴露出的最大的问题。

在内核中的事件等待代码好象是一个协同例程(co-routine)连接。在任何时候,除了一个之外所有的进程都调用了事件等待。剩下的进程是当前运行的那个进程。当它调用事件等待的时候,选择其等待的事件已经发生了的那个进程,并且这个进程从它的到事件等待的调用返回。

接着运行那个可运行的进程? 同每个进程有关的是特权级。系统进程的特权级由发起在一个事件上的等待的代码来赋予。这基本上等同于人们在这样一个事件上所希望的响应(次序)。磁盘事件有高优先级,终端(teletype)事件有低优先级,当日时间(time-of-day)事件有非常低的优先级。(观测的结果是,在系统进程优先级上的不同有很小甚至没有性能冲击。) 所有用户进程优先级都低于最低的系统优先级。用户进程优先级由一个算法来赋予,这个算法基于运算时间总量与这个进程消费的实际时间(real time)的当前比率。在上次的实时单元(unit)中使用了许多运算时间的进程被赋予低用户优先级。因为交互进程以低运算与实际时间比率为特征,(以此)维持交互响应而不用任何特殊安排。

调度算法简单的挑选有最高优先级的进程,所以首先挑出所有系统进程再挑出用户进程。每秒种更新一次运算-实际时间的比率。所以,所有其他东西是平等的,以 1 秒为基本量(quantum)用轮转法调度围成圈的(looping)的用户进程。一个高优先级的进程醒来将抢占一个运行的低优先级进程。这个调度算法有一个非常吸引人的负反馈特征。如果一个进程使用它的高优先级来独占计算机,则它的优先级将被降低。同时,如果一个低优先级进程长时间被忽略,则它的优先级将被提升。

3. I/O 系统

I/O 系统被分成两个完全独立的系统: 块 I/O 系统和字符 I/O 系统。回想起来,名字应当分别是“有组织的 I/O” 和“无组织的 I/O”;尽管术语“块 I/O”有些意义,“字符 I/O”却是完全的用词不当。

设备被特征化(characterize)为一个主设备号,一个次设备号,和一个类别(块或字符)。对于每一类别,有进入设备驱动程序的入口点的一个数组。当调用一个特定设备驱动程序的代码的时候,用主设备号索引这个数组。次设备号作为一个参数而传递给这个设备驱动程序。次设备号除了驱动程序赋予它的特性之外没有任何意义。通常,设备驱动程序使用次设备号来访问一些完全相同的物理设备中一个。

入口点的数组(配置表)作为系统代码和设备驱动程序之间的唯一连接的作用是非常重要的。这个系统的早期版本与驱动程序有一个非常不规范连接,所以手工处置不同配置的系统非常困难。现在有可能在几个小时内建立一个新的设备驱动程序。在多数情况下,这个配置表由读取这个系统的部件清单的一个程序来自动建立。

3.1. 块 I/O 系统

块 I/O 设备的模型由随机寻址的、每个 512 字节的次级存储块组成。这些块被唯一的编址为 0、1、... 直到这个设备的大小。块设备驱动程序的工作是在物理设备上仿真这个模型。

访问块 I/O 设备要通过一层缓冲区(buffer)软件。这个系统维护(典型的 10 到 70 个)缓冲区的一个链表,每个缓冲区都被赋予一个设备名字和一个设备地址。这个缓冲区池为块设备构成了一个数据缓存(cache)。对于一个读请求,缓存查找想要的块。如果找到了这个块,请求者可获得这块数据而不用任何物理 I/O。如果这个块不在缓存中,重命名在缓存中最近最少使用的块,调用正确的设备驱动程序来填充这个被重命名的块,接着就可获得这块数据。以类似的方式处理写请求。找到正确的缓冲区并且在必要时重新标注。通过标记这个缓冲区为“脏”来简单的完成写。物理 I/O 被推迟到这个缓冲区被重命名。

这个方案减少物理 I/O 的利益是相当可观的,特别是考虑到文件系统实现。但是有些缺点。这个算法的异步本质使错误报告和有意义的用户错误处理基本上不可能。UNIX 系统中对 I/O 错误的这种轻率的方式部分的归咎于块 I/O 系统的异步本质。一个次要的问题存在于延迟写中。如果系统异常停止,基本可以肯定在缓冲区中有许多逻辑上完成了,而物理上未完成的 I/O。有一个系统原语从缓冲区中刷新所有外出的(outstanding) I/O 活动。周期性的使用这个原语将有助于但不能解决这个问题。最后,缓冲区间的关联性可以让这个逻辑 I/O 顺序改变了物理 I/O 顺序。这意味着即使软件小心的以正确的次序进行 I/O,磁盘上的数据结构有时还是不一致的。在非随机设备上,尤其是磁带, 颠倒写的顺序是灾难性的。磁带的这个问题是通过每个驱动器只允许一个外出请求写来解决的。

3.2. 字符 I/O 系统

字符 I/O 系统由所有不能归入块 I/O 模型的设备组成。这包括“典型的”字符设备如通信线路、纸带、和行式打印机。还包括磁带和不按老套路使用的磁盘,例如,在磁带上的 80 字节物理记录和每次一磁道的磁盘复制。简短的说,字符 I/O 接口意味着“不是块的所有东西”。来自用户的 I/O 请求被发送到驱动程序而基本上不做变动。这些请求的实现理所当然的依赖于设备驱动程序。有帮助实现特定类型的设备驱动程序的准则和惯例。

3.2.1. 磁盘驱动程序

磁盘驱动程序被实现为带有一个事务(transaction)记录的队列。每个记录持有一个读写标志、一个主存地址、一个次级存储地址、和一个传输字节记数。交换是通过把这样的一个记录传递给交换设备驱动程序来完成的。块 I/O 接口是通过传递带有要填充或清空系统缓冲区的请求的事务记录来实现的。到磁盘驱动程序的字符 I/O 接口建立直接指向用户区域的一个事务记录。建立这种记录的例程还确保用户在这次 I/O 事务期间不被交换出去。这样通过实现通用的磁盘驱动程序,可以把磁盘使用为块设备、字符设备、和交换设备。在普通磁盘驱动程序中唯一真正特定于磁盘的代码是事务的预先排序,用以对一个特定设备最小化延迟,和 I/O 请求的实际上发起。

3.2.2. 字符列表

真正面向字符的设备可以使用处理字符链表的公用代码来实现。字符列表是一个字符的队列。一个例程把一个字符放入一个队列。另一个例程从一个队列取出一个字符。还可能要求知道在一个队列中当前有多少个字符。在系统中所有队列的存储来自一个单一的公用池。在一个队列上放入一个字符将从这个公用池分配空间并把这个字符连接到定义这个队列的数据结构上。从一个队列取出一个字符向这个公用池返还相应的空间。

一个典型的字符输出设备(例如纸带穿孔机)被实现为把来自用户的字符传递到一个字符队列上,直到这个队列上的字符到了某个最大数目。在这个队列上一有东西就激励 I/O 启动,并且一旦启动,则由硬件完成中断来维持。每次有完成中断的时候,驱动程序从这个队列得到下一个字符并把它发送到硬件。检查在这个队列上的字符的数目,若这个数目从一个中间水平上落了下来,则通告一个事件(队列地址)。从用户传递字符到这个队列的进程可以等待这个事件,并在事件发生时把这个队列重新填充为它的最大数目。

以非常类似的方式处理一个典型的字符输入设备(例如纸带阅读器)。

另一类字符设备是终端。终端被表示为三个字符队列。两个输入队列(原始和规范(canonical))和一个输出队列。完全用上述描述的公用代码处理来到一个终端的输出队列上的字符。主要的区别是还有代码来把输出流解释为 ASCII 字符并进行一些转换,例如,为有所欠缺的终端做转义工作。另一个终端的公共特征是在特定控制字符后面插入实时延迟的代码。

终端上的输入有些不同。收集自终端的字符并放置到一个原始输入队列上。这里要处理一些设备相关的代码转换和转义解释。当在原始队列中完成了一行的时候,通告(signal)一个事件。捕获这个信号(signal)接着从这个原始队列复制一行到一个规范队列的代码进行字符删除和行删除(kill)编辑。读在终端上的请求的用户可以直接读取原始或者规范队列。

3.2.3. 其他字符设备

最后,有的设备不符合普通类型。这些设备被设置为字符 I/O 驱动程序。一个例子是把未映射的主存作为 I/O 设备来读写的驱动程序。一些设备作为偶尔的一个字符来对待太快了,但不适合磁盘 I/O 模型。例子有高速通信线路和高速行式打印机。这些设备要么有自己的缓冲区要么“借用”块 I/O 缓冲区一会并接着归还它们。

4. 文件系统

在 UNIX 系统中,文件是一个字节的(一维)数组。系统没有隐含其他的文件结构。文件被附加到目录层次上的任何(并可能多个)地方。目录是用户可以写的简单的文件。文件和目录的外部看法的进一步讨论请参见“unix 分时系统”。

UNIX 文件系统是完全通过块 I/O 系统访问的一个磁盘数据结构。如上所述,“磁盘”的规范看法是一个随机可寻址的 512 字节块的数组。文件系统把磁盘分成四个自我识别的区域(region)。第一块(地址 0)不被文件系统使用。它被留给引导过程。第二块(地址 1)包含“超级块”。这个块,包含磁盘的大小和其他区域的边界,还有其他东西。下一个区域是 i-list,它是文件定义的一个列表。每个文件定义是一个 64 字节的结构,叫做 i-node。一个特定 i-node 在 i-list 中的偏移量叫做它的 i-number。设备名字(主和从设备号)和 i-number 充当一个特定文件的唯一性的名字。在 i-list 之后,直到这个磁盘的结束,是可获得用做文件的内容的自由存储块。

磁盘上的空闲空间由可获得的磁盘块的一个链表来维护。在这个链中的每个块包含链中下一块的磁盘地址。剩余空间包含最多 50 个也是空闲的磁盘块的地址。这样对于一次 I/O 操作,系统获取 50 空闲块和一个找到更多块的指针。磁盘分配算法十分直截了当。因为所有分配都是固定大小块并且有确切数量的空间, 所以没有不需要紧缩和垃圾收集。但是,随着磁盘空间变得分散,延迟在逐步增加。一些安装选择不经常的紧缩磁盘空间来减少延迟。

i-node 包含 13 个磁盘地址。这些地址的前 10 个直接指向这个文件的前 10 个块。如果一个文件大于 10 块 (5,120 字节),则第 11 个地址指向包含这个文件的下 128 个块的地址一个块。如果文件还大于这个尺寸 (70,656 字节),则第 12 块指出最多 128 个块,每个块指出这个文件的 128 个块。更大的(8,459,264 字节)文件使用第 13 个地址用做 “三次间接”地址。这个算法结束于最大的文件大小 1,082,201,087 字节。

通过简单的增加一个新的文件类型来为这个平坦的物理结构增加上逻辑目录层次,这个文件类型就是目录。 目录可以完全作为普通文件来访问。它包含 16 字节的条目,其组成是一个 14 字节的名字和一个 i-number。层次的根有一个周知的 i-number (就是 2)。文件系统结构允许任意的目录有向图,并可以在这个图的任意位置上连接正规文件。实际上,非常早期的 UNIX 系统曾经使用这样一个结构。这样一个结构的管理是很混乱的所以后来的系统被限制为一个目录树。即使是现在,把正规文件多重连接到树中的任意位置,使统计空间成了问题。可能有必要限制整个结构为一个树,并允许一种新形式的连接服从于这个树结构。

文件系统允许轻易建立、删除、随机访问、和空间分配。由于多数物理地址局限于一个小的连续磁盘扇区中,复制、存储、和检查文件系统的一致性也是很容易的。 文件遭受到间接寻址,但是缓存防止了多数隐含的物理 I/O 而不增加许多执行。这个方案的空间开销特性非常好。例如,在一个特定的文件系统上,有 25,000 个文件包含着 130M 字节的数据文件内容。开销(i-node,间接块,和最后的块破碎)大约是 11.5M 。支持这些文件的目录结构有大约 1,500 个目录包含 0.6M 字节的目录内容,和在访问这些目录中的大约 0.5M 字节的开销。汇总到一起,为实际存储的数据带来了小于 10 个百分点的开销。许多系统单在添充的尾随的空白上就有更大的开销。

4.1. 文件系统实现

因为 i-node 定义了文件,文件系统实现的中心围绕着对 i-node 的访问。系统维护所有活跃 i-node 的一个表。在访问一个新文件的时候,系统定位相应的 i-node,分配一个 i-node 表条目,并把这个 i-node 读取到主存中。同在缓冲区缓存中一样,这个表条目被当作这个 i-node 的当前版本。对 i-node 的修改在这个表条目上进行。当对这个 i-node 的最后的访问结束,把这个表条目复制回到次级存储 i-list 并释放这个表条目。

在文件上的所有 I/O 操作在对应的 i-node 表条目的辅助下进行。文件的访问是前面提及的算法的直接实现。用户不用照管 i-node 和 i-number。按照这个目录树的路径来进行文件系统的引用。把一个路径名字转换成一个 i-node 表条目也是直接的。开始于某个已知的 i-node(根或者这个进程的当前目录),通过读这个目录来查找这个路径名的下一个成员。这给出(这个目录的)一个 i-number 和一个隐含的设备。所以可以访问下一个 i-node 表条目。如果它是这个路径名字的最后的成员,则这个 i-node 就是结果。如果不是,这个 i-node 是需要查找的这个路径名字的下一个目录,并重复这个算法。

用户进程用特定原语访问文件系统。其中最常用的是 open、create、read、write、seek、和 close。维护的数据结构展示于图 2。



在与用户有关的系统数据段中,有留给(通常 10 到 50个)打开文件的空间。这个打开文件表由可以用来访问相应的 i-node 表条目的指针组成。与每个打开文件相关联的是一个当前 I/O 指针。这是在这个文件文件上下一次读/写操作的一个字节偏移量。系统把每个读/写请求做为随机操作,它带有对 I/O 指针隐含的定位(seek)。用户通常认为文件是一个序列,它带有的I/O 指针自动统计已经从文件读/写的字节数目。用户当然可以通过在读/写之前通过设置 I/O 指针来进行随机 I/O。

为了文件共享,必须允许有关的进程共享一个公共 I/O 指针,但让访问同一个的文件的无关进程有独立的 I/O 指针。出于这两个条件,I/O 指针不能驻留在 i-node 表或进程的打开文件列表中。为了持有 I/O 指针这个唯一功能而发明了一个新表(打开文件表)。共享同一个打开文件的进程(进行 fork 的结果)共享一个公共打开文件条目。同一个文件的单独打开将只共享 i-node 表条目,而有不同的打开文件表条目。

主要的文件系统原语实现如下。open 把一个文件系统路径转换成一个 i-node 表条目。在新近建立的打开文件表条目中放置到这个 i-node 表条目的一个指针。create 首先建立一个新的 i-node 条目,把这个i-number 写到一个目录中,并接着建立与 open 相同的结构。read 和 write 只是访问上面描述的 i-node 条目。seek 简单的操纵 I/O 指针。不做物理的定位(seeking)。close 只是释放 open 和 create 建立的结构。在打开文件表条目和 i-node 表中保持的引用记数,用来在最后的引用去掉之后释放这些结构。unlink 简单的减少指出给定 i-node 的目录的记数。当到一个 i-node 表条目的最后的引用被去掉的时候,如果没有目录指出这个 i-node,则删除这个文件并释放这个 i-node。文件的延迟删除防止了删除活跃文件所引发的问题。文件可以在仍然打开的时候被删除。在关闭这个文件的时候结果的未命名文件 消失。这是获取临时文件的一种方法。

有一种叫做管道(pipe)的未命名的 FIFO 文件。为了实现先入先出,管道的实现由在每次 read 或 write 之前隐含的 seek 构成。还有检查和同步来防止写者对读者的过度产出和读者对写者的过度取用。

4.2. 挂装文件系统

UNIX 系统的文件系统开始于某个指定块设备、它被格式化为上面描述格式的来包含一个层次结构。这个结构的根是 UNIX 文件系统的根。其他格式化的块设备可以挂装到当前层次的任何叶子上。它在逻辑上延伸了当前的层次结构。挂装的实现是琐碎的。维护一个挂装表来持有成对的叶子 i-node 和块设备。在把一个路径名字转换成一个 i-node 的时候,做一个检查来看新 i-node 是否是一个指定的叶子。如果是,则用这个块设备的根的 i-node 来替换它。

文件的空间分配取自这个文件所在的设备上的空闲池。所以由多个挂装的设备组成的一个文件系统没有空闲次级存储空间的一个公共池。在不同的设备上的这种独立性对于允许容易的卸装一个设备是必须的。

4.3. 其他系统功能

系统为用户做了一点其他事情,如记帐、跟踪/调试、和访问保护。其中多数未被良好的开发,因为在我们计算科学研究中使用的这个系统不需要这些东西。在一些应用中缺少一些特征,例如,良好的进程间通信。

UNIX 内核就应该是一个 I/O 复用器而不是一个完整的操作系统。出于这种观点,UNIX 内核中缺少在多数其他操作系统中可以见到的许多特征。例如,UNIX 内核不支持文件访问方法、文件部署、文件格式、文件最大大小、缓冲池、命令语言、逻辑记录、物理记录、逻辑文件名的分配、逻辑文件名、多个字符集、操作者的控制台,操作者、log-in、或 log-out。其中的许多东西是征状(symptom)而不是特征。其中的多数东西在把内核用做工具的用户软件中实现。它们的一个最佳的例子是命令语言。这个要点使每个用户都可以有他自己的命令语言。维护这种代码与维护用户代码一样容易。用普通用户原语(primitive)实现“系统”代码的想法直接来在 MULTICS。

Unix 分时系统的演化

摘要
本文提供对 Unix 操作系统早期开发的简要描述。它集中在文件系统、进程控制和管道方式命令的想法的演化上。对系统开发期间的社会环境也有所关注。
注意: *本文首次发表于“Language Design and Programming Methodology conference at Sydney, Australia, September 1979”。会议记录作为讲稿出版于“Computer Science #79: Language Design and Programming Methodology, Springer-Verlag, 1980”。这份稿件所基于的重印版本出现在“AT&T Bell Laboratories Technical Journal 63 No. 6 Part 2, October 1984, pp. 1577-93”。

介绍
在过去几年中,Unix 操作系统已经得到广泛的使用,以至于它特别的名字已经成为 Bell 实验室的一个商标。它的重要特征已经被众人所知晓。自从 1974 [1] 中所描述的那个首次发行之后,它经历了大量重写和修补,但是少有原则上更改。但是,Unix 生于 1969 年而不是 1974 年,它的开发历程成了一个鲜为人知并可能有所教益的故事。本文提供系统演化的技术和社会环境的历史记录。

起源
对于 Bell 实验室的计算机科学,1968-1969 年这段时期有些动荡。主要原因是实验室缓慢的、但完全不可避免的、从 Multics 计划中撤出。对于整个实验室计算社区,问题是让 Multics 迅速交付某种可用的系统是日益明显的错误,更不用说最初幻想的那个万能系统了。在此期间,Murray Hill 计算机中心还运行着一个昂贵的 GE 645 机器来不适当的模拟 GE 635。在此期间发生的另一个剧变是计算服务和计算研究在组织上的分离。

出自 Unix 开始时主要涉及到小组(K. Thompson,Ritchie, M. D. McIlroy, J. F. Ossanna)的立场,Multics 的倾覆有着直接感觉到的影响。在 Bell 实验室,我们是在 Multics 坚持实际工作的最后的人,所以我们仍寄希望于它的成功。更重要的是,Multics 对整个社区承诺的方便的交互式计算服务对于我们这个有限的小组是事实上可以获得的,最初曾在 CTSS 系统下开发 Multics,而后来是在 Multics 自身下。尽管 Multics 当时不能支持许多用户,它却能支持我们,尽管有高昂的花销。我们不想失去我们占据的舒适的小环境,因为不能获得类似的东西;甚至在 GE 操作系统下将在以后提供的分时服务都不存在。我们想要保留的不只是在其中编程的一个良好环境,还有可以围绕它组成团队的一个系统。我们获得的经验是公共计算的本质,如提供远程访问、分时机器,不只是在终端上键入程序来替代键盘穿孔机,还有鼓励密切交流。

所以在 1969 年期间, 我们开始尝试找出 Multics 的替代者。找寻采用了多种形式。整个 1969 年我们(主要是 Ossanna、Thompson、Ritchie)密集的游说获得一个中型机器,我们承诺为它写个操作系统;我们建议的机器是 DEC PDP-10 和 SDS (后来的 Xerox) Sigma 7。努力受挫了,我们的提议从未被明确的最终的否决,但也从未被明确的接受。有几次非常接近成功。在我们提出了一个敏锐的复杂提议的时候,对这个努力的最后的打击到来了。谋划要最小化财务费用,涉及到某种直接购买,某种第三方租借,并计划购进一个即将推出的 DEC KA-10 处理器和一个更易接受的 KI-10。提议被拒绝了,传言说 W. O. Baker(研究部的副主管)用‘Bell 实验室不能这样做事!’的评论来反对它。

实际上,回想起来这是非常明显的(并且当时也应该如此),我们要求实验室花费大量的金钱在这么少的人身上去进行这么模糊的一个计划。此外,我非常确定那时操作系统对于我们的管理者不是支持工作的一个有吸引力的领域。他们正在进行的不仅是从开发操作系统的努力失败中解脱自身,还有免于运行本地的计算中心。所以我们建议的买一个机器,一方面可能导致又一个 Multics,另一方面,如果我们生产了有用的东西,会导致他们需要负责又一个计算中心。

在 1969 年除了发生财务困扰之外,还做了技术性工作。在黑板上和潦草的笔记上,Thompson、R. H. Canaday 和 Ritchie 开发了一个文件系统的基本设计,它后来成为 Unix 的心脏。多数设计出自 Thompson,他是思考整个文件系统的推动力,但是我确信我贡献了设备文件的想法。Thompson 对创建一个操作系统的渴望在这个时期经历了多种形式;他还写了(在 Multics 上)对草拟的文件系统设计和程序的分页行为的一个非常详细的模拟。此外,他开始在 GE-645 的一个新操作系统上工作,深入到为这个机器写一个汇编器和一个初步的内核,我所记得它的最大成绩是键入一个问候消息。这个机器是如此的复杂性以至于基本的消息已经是一个非常显著的成就了,但在明确了 645 在实验室的时日要用月来衡量的时候,这项工作被放弃了。

也是在 1969 年期间,Thompson 开发了一个‘太空旅行’游戏。最初在 Multics 上写的,接着转移到 GECOS (GE 和后来的 Honeywell 635 上的操作系统)的 Fortran 中,只是模拟太阳系的主要成员的移动,玩家驾驶飞船四处旅行,观赏风景,并尝试在各个行星和月亮上的登陆。GECOS 版本在两方面是不能让人满意的: 首先,游戏的状态显示是别扭的并难于控制,因为你必须在上面键入命令,还有,游戏在这个大机器上花费大约 $75 的 CPU 时间。它不能占用很长时间,所以 Thompson 找到一个带有卓越的显示处理器的很少使用的 PDP-7 计算机;整个系统被用作一个 Graphic-II 终端。他和我重写了太空旅行来在这台机器上运行。事情比看起来更加野心勃勃;因为我们蔑视所有现存的软件,我们必须写一个浮点算术包,这个显示器的图形字符的逐点指定,和一个调试子系统,它在屏幕角上的位置持续的显示键入的内容。所有这些都是用在 GECOS 下运行的一个交叉汇编器的汇编语言写成的,并生成纸带来传送到 PDP-7 上。

太空旅行,尽管它是一个非常吸引人的游戏,主要用来介入为 PDP-7 准备程序需要的笨拙的技术。不久 Thompson 开始实现以前设计的纸面文件系统(可能‘粉笔文件系统’更准确些)。没有办法实验的一个文件系统是没有结果的一个命题,所以他继续用工作操作系统的其他需要来充实它,特别是进程的概念。接着出现了一小组用户级别实用工具: 复制、打印、删除和编辑文件,当然还有一个简单的命令解释器(shell)。直到这时所有的程序都使用 GECOS 写成并转移到 PDP-7 纸带上;而一旦汇编器完成了系统就可以支持自身了。直到 1970 年 Brian Kernighan 推荐了这个名字‘Unix’,它是‘Multics’有些背叛的双关语,我们今天知道的这个操作系统就诞生了。

PDP-7 Unix 文件系统
在结构上,PDP-7 Unix 的文件系统几乎同今天完全一样。它有:

1)
i-list: i-node 的一个线性数组,每个描述一个文件。i-node 包含的比现在要少,但基本信息是一样的: 文件的保护模式,它的类型和大小,和持有内容的物理块的列表。
2)
目录: 一种特殊的文件,包含名字和相关的 i-number 的一个序列。
3)
描述设备的特殊文件。在 i-node 中不明确的包含设备说明,而是用数字编码: 特定的 i-number 对应于特定的文件。
重要的文件系统调用也在开始时就出现了。Read、write、open、creat (sic)、 close: 有一个非常重要的例外将在后面将讨论,他们都类似于今天你所见到的。一个次要的区别是 I/O 的单位是字而不是字节,因为 PDP-7 是一个字寻址的机器。在实践中仅意味着处理字符流的所有程序都要忽略 null 字符,因为 null 被用来把文件填充成偶数个字符。另一个次要的区别,偶尔讨厌的区别是终端缺乏字和行删除处理。终端实际上总是在原始模式下。只有一些程序(特别是 shell 和编辑器)费力的去实现删除处理。

尽管相当类似于目前的文件系统,PDP-7 文件系统在一方面上有显著的不同: 这里没有路径名字,给系统的每个文件名字参数都是相对于当前目录的简单的名字(没有‘/’)。在通常 Unix 意义上的连接不存在。与一组精心安排的约定一起,它们是使路径名的缺乏能够被接受的主要方式。

link 调用有如下形式

link(dir, file, newname)

这里的 dir 是在当前目录中的一个目录文件,file 是在那个目录中的一个现存的条目,而 newname 是这个连接的名字,它被增加到当前目录。因为 dir 必须在当前目录中,很明显的,现在的禁止到目录的连接还不是强制的;PDP-7 Unix 文件系统是一般有向图的形状。
所以每个用户不需要维护到所有感兴趣的目录的一个连接,有一个叫做 dd 的目录为每个用户包含一个目录条目。这样,要制作到在 ken 目录中 x 的文件的一个连接,我可以做

ln dd ken ken
ln ken x x
rm ken

这个方案提供了非常难用的子目录,这使它们在实践中不被使用。另一个重要的阻碍是在系统运行期间无法建立目录;所有的目录都是在从纸带重建文件系统的时候建立的,所以目录实际上是不可更新的资源。
dd 约定使 chdir 命令相对的方便了。它接受多个参数,并把当前目录依次切换成每个指名目录。所以

chdir dd ken

将移动到目录 ken。(顺便提一下,chdir 拼写为 ch;但我不记得在我们转到 PDP-11 的时候为什么要展开它。)
这个文件系统实现的最严重的麻烦,除了缺乏路径名字之外,还有难于更改它的配置;如前所述,目录和特殊文件只能在磁盘建立的时候制作。安装一个新设备是非常痛苦的,因为设备的代码广泛的散布系统中;例如有依次访问每个设备的多个循环。不要惊讶,没有挂装可移动磁盘组件的想法,因为这个机器只有一个单一的 fixed-head 磁盘。

实现文件系统的操作系统代码是当前方案的彻底的简化版本。一个重要的简化来自系统不是多道程序(multi-programmed)的事实;内存中在一个时刻只有一个程序,并只在发生对换的时候在进程之间传递控制。所以,例如这里有一个 iget 例程使一个指名的 i-node 可获得,但它把这个 i-node 留在一个恒定的、静态的位置中,而不是返回到一个活跃 i-node 的大表的一个指针。存在着当前的缓冲机制的祖先(有大约 4 个缓冲区),但本质上没有磁盘 I/O 和计算的重叠。这不只是为了简化而避免的。附加在 PDP-7 上的磁盘在那时是很快的;它每 2 微秒传输一个18-位字。另一方面,PDP-7 自身有一个 1 微秒的内存周期节拍,并且多数指令占用 2 个周期(一个用于指令自身,一个用于操作数)。但是,间接寻址指令要求 3 个周期,并且间接寻址是很常用的,原因是这个机器没有变址寄存器。最后,DMA 控制器不能在指令运行期间访问内存。结果是,如果在磁盘传输期间执行了任何间接寻址的指令,则磁盘就会招致溢出丢失(overrun)。这样在磁盘正在运行的时候,控制不能返回给用户,事实上也不能执行一般的系统代码。时钟和终端的中断例程,它们需要在所有时候都是可运行的,必须以非常奇怪的方式编码来避免间接寻址。

进程控制
对于‘进程控制’,我解释为进程建立和使用的机制;今天的系统调用 fork、exec、wait 和 exit 实现了这些机制。不象文件系统,它基本上在最近的时候就存在,进程控制方案在 PDP-7 Unix 已经使用之后经历了显著的突变。(在 PDP-11 系统中路径名的介入的确是一个可观的表示法改进,但不是基本结构上的改变。)

今天,shell 执行命令的方式可以总结如下:

1)
shell 从终端读取一行命令。
2)
它用 fork 建立一个子进程。
3)
子进程使用 exec 来从文件调入这个命令。
4)
此时,父 shell 使用 wait 来等待子(命令)进程通过调用 exit 来终止。
5)
父 shell 回到步骤 1)。
进程(独立执行的实体)存在于非常早期的 PDP-7 Unix 中。事实上正好有两个,附属于这个机器的两个终端一人一个。这里没有 fork、wait 或 exec。这里有 exit,但是它的意思是非常不同的,一会就能见到。shell 的主循环按下面这样运行。

1)
shell 关闭所有它打开的文件,接着是为标准输入和输出(文件描述符 0 和 1)打开的终端特殊文件。
2)
它从终端读一行命令。
3)
它连接到命令详细指定这个命令的文件,打开这个文件,并删除这个连接。接着它复制一个小引导程序到内存的顶端并跳转到那里;这个引导程序读入这个文件覆盖 shell 代码,接着跳转到这个命令的最先的位置(效果上是 exec)。
4)
这个命令做它的工作,接着通过调用 exit 终止。exit 调用导致系统读入 shell 的新鲜的拷贝覆盖终止了的命令,接着跳转到它的开始(这在效果上等同于跳转到步骤 1)。
关于这个原始实现的最有趣的事情是它预示(anticipate)了后来这个主题充分开发的程度。真的,它不支持后台进程,也不支持 shell 命令文件(更不用说管道和过滤器);但是 IO 重定向(通过‘<’和‘>’)不久就有了;下面会讨论它。重定向的实现非常直接;在上面步骤 3) shell 只是把它的标准输入或输出替代为适当的文件。对后续开发最至关紧要的是把 shell 实现为存储在一个文件中的一个用户级别程序,而不是操作系统的一部分。

这个进程控制方案的结构,是每个终端一个进程,类似于许多交互式系统,例如 CTSS、Multics、Honeywell TSS 和 IBM TSS 及 TSO。一般的这种系统要求特殊的机制来实现有用的设施,比如分开的计算和命令文件;Unix 在这个发展阶段不费心去提供这种特殊机制。它还展示了一些恼人的、特殊性质的问题。例如,一个新近重建的 shell 必须关闭所有它打开的文件,除去刚才执行的命令所有打开的文件,废除以前的 IO 重定向。接着它必须重新打开对应于它的终端的特殊文件,来读取一个新命令行。没有 /dev 目录(因为没有目录名字);此外,shell 不能跨越命令保留任何记忆,因为在每个命令之后它都被重新再次执行。这样需要一个进一步的文件系统约定: 每个目录都必须为对应于打开它的进程的终端包含一个 tty 条目。如果你意外的进入缺失这条目的一个目录,shell 将无望的循环;唯一的补救就是重新启动。(有时缺失连接可能是其他终端造成的。)

现代形式的进程控制是在几天之内设计并实现的。令人惊异的是它如此容易的适合现存系统;同时也很容易看出来,设计的某些稍微不同寻常的特征恰好出现,是因为它们体现了对现存的东西做简单的、易于编码的变更。一个好的例子是 fork 和 exec 功能的独立。建立新进程的最通用的模型涉及指定一个程序让这个进程来执行;在 Unix 中,一个复制的(forked)进程继续运行同它的父亲一样的程序直到它进行一次明确的 exec。这种功能的独立的确不是 Unix 独一无二的,并且实际上它是在 Berkeley 分时系统[2] 中出现的,Thompson 很熟悉它。尽管如此,它存在于 Unix 中最合理的原因主要是可以容易的实现 fork 而不用很多其他东西。系统已经处理多个(这时是两个)进程;有一个进程表,并且进程在主存和磁盘之间对换。fork 的最初实现只要求

1)
扩充进程表。
2)
增加一个 fork 调用,使用现存的对换 IO 原语把当前进程复制到磁盘对换区域,并对进程表作一些调整。
事实上,PDP-7 的 fork 调用需要正好 27 行汇编代码。当然,需要操作系统和用户程序的一些其他变更,并且其中一些是非常有趣和意外的。此外组合的 fork-exec 相当的复杂,好像是因为 exec 同样的不存在;shell 使用明显的 IO 已经完成了它的功能。

exit 系统调用,它以前是读入 shell 的一个新拷贝(实际上是一种自动的没有参数的 exec),相当的简单;在新版本中进程只需要清除它的进程表条目,并放弃控制。

奇怪的是,变成 wait 的原语比当前方案更加一般化。一对原语在指名的进程之间发送一个字的消息:

smes(pid, message)
(pid, message) = rmes()

smes 的目标进程不需要与接收者有任何父子相传的关系,除了 fork 向父进程和子进程返回与它们有关的 ID 之外,系统不提供任何交流进程 ID 的明显的机制。消息不排队,发送者延迟直到接收者读到这个消息。
消息设施使用如下: 父 shell,在建立一个进程去执行一个命令之后,通过 smes 发送一个消息到这个新进程;在这命令终止的时候(假定它不尝试读任何消息),这个 shell 的阻塞的 smes 调用返回一个错误指示目标进程不存在。所以 shell 的 smes 在效果上等价于 wait.

一个不同的协议,它更多的利用了消息提供的通用性,是在初始化程序和每个终端的 shell 之间使用的。初始化进程,它的 ID 被当作是 1,为每个终端建立一个 shell,并接着发起 rmes;每个 shell,在它读到它的输入文件的尽头的时候,使用 smes 来发送一个常规的‘I am terminating’消息到初始化进程,它为这些终端重建一个新的 shell 进程。

我不能想起消息的其他用途。这解释了为什么这个设施被当前系统的 wait 调用所替代,少了通用性,但更直接的适用于想要的目的。还可能与这个机制中明显的缺陷有关: 如果一个命令进程尝试使用消息来与其他进程通信,它将破坏 shell 的同步。shell 依赖发送用不被接收的消息;如果一个命令执行了 rmes,它将收到 shell 的假冒消息,并导致 shell 读另一行输入,如同这个命令已经终止了一样。如果证明需要通用性的消息,则这个缺陷可能已经被修补好了。

在某种程度上,新的进程控制方案立即呈现出某些非常有价值的、但对实现不重要的特征;例如分离的进程(使用‘&’) 和递归的把 shell 作为命令使用。多数系统必须为文件提供某种特定的‘批作业子任务’设施和特定的一个命令解释器以区别于交互使用的命令解释器。

尽管多进程的想法确实非常容易的就介入(slipped in)了,但有些后果是未预期到的。其中最难忘的是在新系统提出并显然的工作了之后不久就变得明显了。在我们庆祝的时候,发现 chdir (改变当前目录)命令停止了工作。关于增补的 fork 如何破坏 chdir 调用做了大量的代码阅读和观念的反省。最终真相揭晓了: 在老系统中 chdir 是一个普通命令;它调整附属在这终端上的进程的(唯一的)进程的当前目录。在新系统中,chdir 命令正确的改变了为执行它而建立的进程的目录,但是这个进程迅速就终止了并对它的父shell 没有任何影响! 必须使 chdir 成为一个特殊命令,在 shell 内部执行。找出了许多命令式的功能有同样的性质,例如 login。

在系统过去和新的进程控制方案之间的另一个不匹配花了很长时间才变得明显。最初,与每个打开的文件相关联的读/写指针存储在打开这个文件的进程中。(这个指针指示在文件中下次读或写发生的位置。) 这个组织的问题只在我们尝试使用命令文件的时候才变得明显。假定一个简单的命令文件包含

ls
who

并被如下执行:

sh comfile >output

事件序列是

1)
主 shell 建立一个新进程,它打开 outfile 来接收标准并递归的执行 shell。
2)
新 shell 建立另一个 process 来执行 ls,它正确的写在文件 output 上并接着终止。
3)
建立另一个进程来执行下一个命令。但是,output 的 IO 指针是从 shell 复制来的,它仍是 0,因为 shell 从未在它的输出上写过了,而 IO 指针是与进程相关联的。效果上是 who 的输出覆写并销毁了前面的 ls 命令的输出。
这个问题的解决要求建立一个新的系统表来持有打开文件的 IO 指针,独立于打开它们的进程。

IO 重定向
使用‘>’和‘<’字符的、非常方便的 IO 重定向符号在最早的 PDP-7 Unix 系统中不存在,但是它确实出现的很早。象 Unix 的多数其他部分,它的灵感来自 Multics 的思想。Multics 有非常通用的 IO 重定向机制 [3],具体为命名 IO 流,它可以被动态重定向到各种设备、文件、甚至通过特殊的流处理模块。甚至在我们十年前熟悉的 Multics 版本中,就存在一个命令把后续的正常预定到终端的输出切换文件上,和另一个命令来把输出重新附属到终端上。在 Unix 下可以写 ls >xx

来得到在 xx 中的你的文件的一个列表,在 Multics 上表示为

iocall attach user_output file xx
list
iocall attach user_output syn user_i/o

尽管这是在 Multics 时代经常使用的笨拙的序列,并被完全直接集成到 Multics shell 中,在那时这个想法并未出现在我们或其他任何人那里。我推测其原因是 Multics 计划的 sheer size: IO 系统的实现者是在 Murray Hill 的 Bell Labs,而 shell 是在 MIT 做的。我们不能考虑更改那个 shell(那是他们的程序);相对的,shell 的监管者可能不知道 iocall 的这种使用,尽管笨拙。(The 1969 Multics manual [4] 列出 iocall 作为一个“作者维护”的非标准命令)。因为 Unix IO 系统和它的 shell 是在 Thompson 的独自控制之下,在正确想法浮出水面的时候,实现它只是大约一个小时的事情。
PDP-11 的出现
在 1970 年初,PDP-7 Unix 是正在消逝的关注。按今天的标准它是原始的,但它仍有能力比它的替代者提供更合意的编程环境。不过,很明显的是, PDP-7 这个我们不再拥有的机器,已经废弃了,在同一行列的后继者也没什么意思。在 1970 年早期我们提议获得一个 PDP-11,它刚由 Digital 推出。在某种意义上,这个提议只不过是从几年就开始做的一系列尝试中最新的一次。在两个重要的方面有所不同。首先,钱数(大约 $65,000)是比我们以前要求的少一个数量级;其次,达成的契约不只是写某种(未指定的)操作系统,而是建立一个系统,明确的设计用来编辑和格式化文本,现在这叫做‘字处理系统’。这个提议的推动力主要来自 J. F. Ossanna,他毕生致力于文本处理。如果说我们早先的提议太含混了,这个提议可能就太明确了;最初它也受到冷遇。但不久,通过 L. E. McMahon 的努力而获得了资金,在五月是填了一份 PDP-11 订单。

夏天结束的时候处理器到来了,但 PDP-11 是个新产品以至于直到 12 月还没获得磁盘。在此期间,在 PDP-7 上使用交叉编译器写了一个根本的、只在内存的 Unix。多数时候,这个机器呆在角落里, 做三个月的作业列举在 6×8 棋盘上所有闭合的跳马巡回。

第一个 PDP-11 系统
一旦磁盘到来,系统迅速的完成了。在内部结构中,PDP-11 Unix 的第一个版本只体现了比 PDP-7 系统相对次要的改进;写它主要是大量转移工作。例如,没有多道程序;在任何时候在内存中只存在一个用户的程序。在另一方面,在到用户的界面上有重要的改变: 出现了现在的目录结构,带有完整的路径名字, 还有现代的形式 exec 和 wait,和便利设施如终端的字符删除和行删除处理。可能对企业最有趣的事情是它的小尺寸: 有 24K 字节的内存(16K 给系统,8K 给用户),和有 1K 块(512K 字节)的一个磁盘。文件限制到 64K 字节。

在定了 PDP-11 的订单的时候,好像是很自然的,或者是权宜之计,承诺了专注于字处理的一个系统。在硬件推延到来期间,PDP-7 Unix 日益增长的使用证明建立 PDP-11 Unix 作为开发工具是正确的,适合在写一个更加专用的系统时使用。到了 1971 年春天,普遍的认同了没有人会对零碎的 Unix 有一点兴趣。所以,我们把 roff 文本格式化器转移到了 PDP-11 汇编语言,起步于从 McIlroy 在 Multics 上的 BCPL 版本转移来的 PDP-7 版本,它受到 J. Saltzer 在 CTSS 上的 runoff 程序的影响。在初夏,编辑器和格式化器到手了,我们准备好履行我们的契约为专利部准备专利应用提供文本处理服务。当时,他们正在为了这个目的评估一个商用系统;我们提供的主要优势(除了参与一个内部实验的疑点之外)有两点: 首先,我们支持 Teletype 的 model 37 终端,它带有一个扩展的活字箱,可以打印他们需要的多数数学符号;其次,我们迅速的赋予 roff 生成行编号页面的能力,这是专利办公室需要的而其他系统不能处理的。

在 1971 年的后半段,我们支持专利部的三个打字员,他们整天繁忙的打字、编辑并格式化专利应用,同时我们尝试完成自己的工作。Unix 获得了在适度的硬件上提供有价值的服务的声誉,并且这个时期可能在利益/设备比率上标记了一个顶点;在没有内存保护并只有一个单一的 .5 MB 磁盘的一个机器上,每个新程序的测试都需要细心和大胆,因为这可能轻易的使系统崩溃,打字员的几个小时的工作都意味着把更多信息推出到 DECtape 上,原因是磁盘非常小。

实验在尝试但很成功。不仅是专利部接受了 Unix,并成为在实验室中众多小组中第一个认可我们工作的,而且我们得到了充分的信任度,说服我们的主管去获得第一个 PDP 11/45 系统。我们从此集聚了很多硬件,并持续的致力于软件,但因为多数有价值的工作已经发表了,(例如在系统自身[1, 5, 6, 7, 8, 9]),这里没有必要重复。

管道
Unix 对操作系统和命令语言文化的最受称赞的贡献就是管道,用在命令的管道线中。当然,基本想法决不是新的;管道只是协同例程(coroutine)的一种特殊形式。甚至实现都不是空前的,尽管我们当时不知道它;Dartmouth 的分时系统[10]的‘通信文件’与 Unix 管道非常类似,但他们好像没有这么完整的开发使用。

管道在 1972 年出现于 Unix,正好在系统的 PDP-11 版本运转之后,来自 M. D. McIlroy 的建议(或者是坚决主张),它是以协同例程为特征的非层次控制流的的长期倡导者。在管道实现的很多年前,他就建议命令应当被当作二元操作符来考虑,它的左右操作数指定输入和输出文件。这样一个‘copy’实用工具将是如下的命令

inputfile copy outputfile

要做一个管道线,命令操作符可以堆叠起来。这样,要排序 input,整齐的标页数,并离线打印结果,你可以写

input sort paginate offprint

在今天的系统中,这将对应于
sort input pr opr

这个想法,在黑板上解释了一个下午,激发了我们的兴趣但没有燃起任何立即的行动。对这个想法提出了几个异议: 中缀表示法好像太激进了(我们更习惯于键入‘cp x y’来复制 x 到 y);并且我们不知如何区分命令参数和输入或输出文件。还有,命令执行的一个输入一个输出的模式好像太局限了。多么缺乏想象力呀!
一段时间之后,感谢 McIlroy 的坚持,管道最终被安装到操作系统中(一个相对简单的工作),并介入了新的表示法。它使用与同 I/O 重定向相同的字符。例如,上述管道线可以写为

sort input >pr>opr>

想法是紧随‘>’之后的可以是一个文件,来制定输出到这个文件的重定向,或一个命令,把前面的命令的输出定向为这个命令的输入。在这个例子中需要尾随的‘>’来指定 opr 的(不存在的)输出应当被定向到控制台;否则命令 opr 根本就不执行;转而建立一个文件 opr。
新设施被狂热的认可了,并且术语‘过滤器’马上就出现了。更改了许多命令来在管道线中使用。例如,没有人能想象到有人需要 sort 或 pr 实用工具在没有给出明显的参数的时候排序或打印它的标准输入。

不久这个表示法的一些问题就变得明显了。最讨厌的是一个愚蠢的词法问题: 在‘>’之后的字符串是用空白来界定的,所以要在例子中给 pr 一个参数,你必须引用:

sort input >"pr -2">opr>

其次,尝试给予一般性,管道表示法接受‘<’作为输入重定向在某种程度上对应于‘>’;这意味着这个表示法是不一致的。例如你还可以写,
opr <"sort>opr

使用‘<’和‘>’的管道表示法只幸存了几个月;它被替代为提供给你一个唯一的操作符来分隔一个管道线的成员。尽管老的表示法有特有的魔力和内在的一致性,新的表示法的确更加出众。当然,它也有局限性。 它是不加遮掩的线性的,尽管在有的情况下要求多个重定向的输入和输出。例如,什么是比较两个程序的输出的最佳方式? 什么是调用有两个并行输出流的程序的正确表示法?
我在上面的 IO 重定向章节提及到 Multics 提供一种机制,IO 流可以定向通过处理模块,在去到(或来自)设备或文件的路途上作为发起者或接受者。因此好像 Multics 中的流接合是 Unix 管道的直接先驱,如同 Multics IO 重定向的确是 Unix 版本的先驱。事实上我不认为这是真的,或者只在微弱的意义上是真的。不仅协同例程已经是众所周知的,而且他们的具体化为 Multics 可接合的 IO 模块要求特殊的编码,以至于不能用于其它任何目的。Unix 管道线的天才之处恰好是它用经常以单一的方式使用的命令构造出来的。需要看到这个可能性并发明这个表示法的精神飞跃是真正巨大的。

高级语言
最初的 PDP-7 Unix 系统的所有程序是用汇编语言写成的,并且是赤裸的汇编语言,这里没有宏。此外,没有装载器或连接器,所以每个程序都必须是自身完整的。出现的第一个有趣的语言是 McIlroy 实现的 McClure 的 TMG[11] 的一个版本。在获得了 TMG 的时候,Thompson 决定不能装作提供实际的计算服务而不提供 Fortran,所以他坐下来用 TMG 写一个 Fortran。我记得,处理 Fortran 的企图持续了一周。他转而创作了一个新语言 B[12] 的定义和一个编译器。B 受 BCPL[13] 语言的深刻影响;其他影响是 Thompson 对 spartan 语法的尝试,和编译器必须适应的非常小的空间。这个编译器生成简单的解释性代码;尽管它和它生成的程序非常慢,它却使生命更加舒适了。一旦获得了到常规的系统调用的接口,我们立即就开始享受到使用一个合理的语言来写通常叫做‘系统程序’的东西的乐趣:它们是编译器、汇编器、和连接器。(尽管有人认为我们在 Multics 下使用的 PL/I 是不切合实际的,它却比汇编语言好的多。) 在其他程序中,PDP-7 B 的 PDP-11 交叉编译器是用 B 写的,并且经过了一段时间之后,PDP-7 自身的 B 编译器也从 TMG 转移到了 B。

在 PDP-11 到来的时候,B 几乎立即就转移到它上面了。实际上,多精度‘桌面计算器’程序 dc 的一个版本是最先在 PDP-11 上运行的程序之一,恰好在磁盘到来之前。但是,B 没有立即接管下来。只有短暂的想法用 B 重写操作系统而不是汇编器,对于多数实用工具也是同样的。甚至汇编器都用汇编语言重写。采用这个途径主要是因为解释性代码的缓慢。更小但仍在实际上很重要的是面向字的 B 语言和字节寻址的 PDP-11 之间的不匹配。

所以,在 1971 年,开始做的工作了变成了 C 语言[14]。从 BCPL 到 B 到 C 的语言开发的故事在别的地方[15]讲述了,不需要在这里重复。可能最重要的分水岭出现在 1973 年期间,这时操作系统内核用 C 重写了。在这个转折点上系统呈现了它的现代形式;最深远的改变是引入了多道程序。还有一些外在可见的改变,此外系统的内部结构变得更加合理和全面。这些努力的成功使我们确信 C 是给系统编程的一个几乎通用的工具,而不只是给简单应用的玩具。

今天,仍用汇编写的唯一重要的 Unix 程序就是汇编器自身;事实上所有的实用工具程序是用 C 写的,多数应用程序也是如此,尽管这是 Fortran、Pascal、和 Algol 68 等的领地。毫无疑问,Unix 的成功多数来自可读性,可修改性,和它的软件的可移植性,而这都来自它们的用高级语言表达。

结论
关于陈年回忆的令人欣慰的事情之一就是他们倾向于呈现出玫瑰色的光芒。当在这里描述的时候,Unix 的早期版本所提供的编程环境好像是非常粗糙和原始的。我确信如果强行回到 PDP-7 我将发现它难以忍受的限制和缺乏方便的工具。然而,在当时好像不是这样;记忆决定了什么是好的和什么是持久的,和帮助创造使生命更美好的改进的乐趣。十年后,我希望我们回头看看,依然有同样的进步和连续结合在一起的混和感觉。

致谢
我要感谢 S. P. Morgan、K. Thompson 和 M. D. McIlroy 提供了早期的文档并挖掘了记忆。

因为我对描述想法的演化最有兴趣,本文只在非常重要的地方把想法和工作归属于个人。读者一般来说不应有所误解,这里不明确的指代词‘我们’指的是‘Thompson 和做一些协助的我’。

引用
1.
D. M. Ritchie and K. Thompson, `The Unix Time-sharing System, C. ACM 17 No. 7 (July 1974), pp 365-37.
2.
L. P. Deutch and B. W. Lampson, `SDS 930 Time-sharing System Preliminary Reference Manual,' Doc. 30.10.10, Project Genie, Univ. Cal. at Berkeley (April 1965).
3.
R. J. Feiertag and E. I. Organick, `The Multics input-output system,' Proc. Third Symposium on Operating Systems Principles, October 18-20, 1971, pp. 35-41.
4.
The Multiplexed Information and Computing Service: Programmers' Manual, Mass. Inst. of Technology, Project MAC, Cambridge MA, (1969).
5.
K. Thompson, `Unix Implementation,' Bell System Tech J. 57 No. 6, (July-August 1978), pp. 1931-46.
6.
S. C. Johnson and D. M. Ritchie, Portability of C Programs and the Unix System,' Bell System Tech J. 57 No. 6, (July-August 1978), pp. 2021-48.
7.
B. W. Kernighan, M. E. Lesk, and J. F. Ossanna. `Document Preparation,' Bell Sys. Tech. J., 57 No. 6, pp. 2115-2135.
8.
B. W. Kernighan and L. L. Cherry, `A System for Typesetting Mathematics,' J. Comm. Assoc. Comp. Mach. 18, pp. 151-157 (March 1975).
9.
M. E. Lesk and B. W. Kernighan, `Computer Typesetting of Technical Journals on Unix,' Proc. AFIPS NCC 46 (1977), pp. 879-88.
10.
Systems Programmers Manual for the Dartmouth Time Sharing System for the GE 635 Computer, Dartmouth College, Hanover, New Hampshire, 1971.
11.
R. M. McClure, `TMG--A Syntax-Directed Compiler,' Proc 20th ACM National Conf. (1968), pp. 262-74.
12.
S. C. Johnson and B. W. Kernighan, `The Programming Language B,' Comp. Sci. Tech. Rep. #8, Bell Laboratories, Murray Hill NJ (1973).
13.
M. Richards, `BCPL: A Tool for Compiler Writing and Systems Programming,' Proc. AFIPS SJCC 34 (1969), pp. 557-66.
14.
B. W. Kernighan and D. M. Ritchie, The C Programming Language, Prentice-Hall, Englewood Cliffs NJ, 1978. Second Edition, 1979.
15.
D. M. Ritchie, S. C. Johnson, and M. E. Lesk, `The C Programming Language,' Bell Sys. Tech. J. 57 No. 6 (July-August 1978) pp. 1991-2019.

2008年4月25日星期五

linux操作系统

Linux操作系统,是一种计算机操作系统。Linux操作系统的内核的名字也是“Linux”。Linux操作系统也是自由软件和开放源代码发展中最著名的例子。

严格来讲,Linux这个词本身只表示Linux内核,但在实际上人们已经习惯了用Linux来形容整个基于Linux内核,并且搭配了程各种人机界面、应用和服务软件的操作系统(也被称为GNU/Linux)。基于这些组件的Linux软件被称为Linux发行版。一般来讲,一个Linux发行套件包含大量的软件,比如软件开发工具,数据库,Web服务器(例如Apache),X Window,桌面环境(比如GNOME和KDE),办公套件(比如OpenOffice.org),等等。
Linux内核最初是为英特尔386微处理器设计的。现在Linux内核支持从个人电脑到大型主机甚至包括嵌入式系统在内的各种硬件设备。
在开始的时候,Linux只是个人狂热爱好的一种产物。但是现在,Linux已经成为了一种受到广泛关注和支持的一种操作系统。包括IBM和惠普在内的一些计算机业巨头也开始支持Linux。很多人认为,和其他的商用Unix系统以及微软Windows相比,作为自由软件的Linux具有低成本,安全性高,更加可信赖的优势。

一、概述

绝大多数基于Linux内核的的操作系统使用了大量的GNU软件,包括了shell程序、工具、程序库、编译器及工具,还有许多其他程序,例如Emacs。正因为如此,GNU计划的开创者理查德·马修·斯托曼博士提议将Linux操作系统改名为GNU/Linux。但有些人只把操作系统叫做"Linux"。
大多数系统还包括了像提供GUI界面的XFree86之类的曾经运行于BSD的程序。
除了一部分专家之外,大多数人都不自己选择每一样组件或自行设置,而是直接使用Linux套件。

1983年,理察·马修·斯托曼(Richard Stallman)创立了GNU计划(GNU Project)。这个计划有一个目标是为了发展一个完全免费自由的 Unix-like 操作系统。自1990年代发起这个计划以来,GNU 开始大量的产生或收集各种系统所必备的元件,像是——函式库(libraries)、编译器(compilers)、侦错工具(debuggers)、文字编辑器(text editors)、网页服务器(web server),以及一个Unix的使用者接口(Unix shell)——除了执行核心(kernel)仍然付阙外。1990年,GNU计划开始在马赫微核(Mach microkernel)的架构之上开发系统核心,也就是所谓的GNU Hurd,但是这个基于Mach的设计异常复杂,发展进度则相对缓慢。
Linux的基本思想有两点:第一,一切都是文件;第二,每个软件都有确定的用途,同时它们都尽可能被编写得更好。其中第一条详细来讲就是系统中的所有都归结为一个文件,包括命令、硬件和软件设备、操作系统、进程等等对于操作系统内核而言,都被视为拥有各自特性或类型的文件。至于说Linux是基于 Unix的,很大程度上也是因为这两者的基本思想十分相近。

Linux内核最初只是由芬兰人李纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的,当时他并不满意Minix这个教学用的操作系统。最初的设想中,Linux 是一种类似Minix这样的一种操作系统。Linux的第一个版本在1991年9月被大学FTP server管理员Ari Lemmke发布在Internet上,最初Torvalds 称这个核心的名称为 "Freax" ,意思是自由( "free")和奇异( "freak") 的结合字,并且附上了"X"这个常用的字母,以配合所谓的 Unix-like 的系统。但是FTP server管理员嫌原来的命名“Freax”的名称不好听,把核心的称呼改成“Linux”,当时仅有10000行代码,仍必须执行于Minix操作系统之上,并且必须使用硬盘开机;随后在10月份第二个版本(0.02版)就发布了,同时这位芬兰赫尔辛基的大学生在comp.os.minix上发布一则信息:

Hello everybody out there using minix-
I'm doing a (free) operation system (just a hobby,
won't be big and professional like gnu) for 386(486) AT clones.

二、历史

Linux 的历史是和GNU紧密联系在一起的。从1983年开始的GNU计划致力于开发一个自由并且完整的类Unix操作系统,包括软件开发工具和各种应用程序。到1991年 Linux 内核发布的时候,GNU已经几乎完成了除了系统内核之外的各种必备软件的开发。在Linus Torvalds和其他开发人员的努力下,GNU组件可以运行于Linux内核之上。整个内核是基于 GNU 通用公共许可,也就是GPL(GNU General Public License,GNU通用公共许可证)的,但是Linux内核并不是GNU 计划的一部分。1994年3月,Linux1.0版正式发布,Marc Ewing成立了 Red Hat 软件公司,成为最著名的 Linux 分销商之一。

Unix & Linux历史源流早期Linux的开机管理程序(boot loader)是使用LILO(Linux Loader),存在着一些难以容忍的缺陷,例如无法识别8G以外的硬盘,后来新增GRUB(GRand Unified Bootloader)克服了这些缺点,具有‘动态搜寻核心档案’的功能,可以让您在开机的时候,可以自行编辑您的开机设定系统档案,透过 ext2 或 ext3 档案系统中载入 Linux Kernel。
Linux 的标志和吉祥物是一只名字叫做 Tux 的 企鹅,标志的由来是因为Linus在澳洲时曾被一只动物园里的企鹅咬了一口,便选择了企鹅作为Linux的标志。Linux的注册商标是Linus Torvalds所有的。这是由于在1996年,一个名字叫做William R. Della Croce的律师开始向各个 Linux 发布商发信,声明他拥有Linux商标的所有权,并且要求各个发布商支付版税,这些发行商集体进行上诉,要求将该注册商标重新分配给 Linus Torvalds。Linus Torvalds 一再声明 Linux 是免费的,他本人可以卖掉,但 Linux 绝不能卖。
Linux发行版的某些版本是不需要安装,只需通过CD或者可启动的USB存储设备就能使用的版本,他们称为 LiveCD。

三、用户群

GNU/Linux爱好者设计的宣传图,并使用GPL授权
GNU/Linux社群设计的立体图,并使用GPL授权传统的Linux用户一般都安装并设置自己的操作系统,他们往往比其他操作系统,例如微软Windows和Mac OS的用户更有经验。这些用户有时被称作“黑客”或是“极客”(geek)。然而随着Linux越来越流行,越来越多的原厂委托制造(OEM) 开始在其销售的电脑上预装上Linux,Linux的用户中也有了普通电脑用户,Linux系统也开始慢慢抢占桌面电脑操作系统市场。同时Linux也是最受欢迎的服务器操作系统之一。Linux也在嵌入式电脑市场上拥有优势,低成本的特性使Linux深受用户欢迎。使用Linux主要的成本为移植、培训和学习的费用,早期由于会使用Linux的人较少,这方面费用较高,但这方面的费用已经随着Linux的日益普及和Linux上的软件越来越多、越来越方便而降低。
KDE和GNOME等桌面系统使Linux更像是一个Mac或Windows之类的操作系统,提供完善的图形用户界面,而不同于其他使用命令行界面(Command Line Interface,CLI)的类Unix操作系统。

四、未来软件界的方向

Linux 作为较早的源代码开放操作系统,将引领未来软件发展的方向。
基于 Linux 开放源码的特性,越来越多大中型企业及政府投入更多的资源来开发 Linux。现今世界上,很多国家逐渐的把政府机构内部门的电脑转移到 Linux 上,这个情况还会一直持续。Linux 的广泛使用为政府机构节省了不少经费,也降低了对封闭源码软件潜在的安全性的忧虑。



五、Linux的英文发音

根据Torvalds的说法,Linux的发音和“Minix”是押韵的。
“Li”中“i”的发音类似于“Minix”中“i”的发音,而“nux”中“u”的发音类似于英文单词“profess”中“o”的发音。依照国际音标应该是['linэks]——类似于“喱呐科斯”
有一份Torvalds本人说话的音频,音频内容为“Hello, this is Linus Torvalds, and I pronounce Linux as Linux”。
下载地址http://www.linuxforum.net/books/LinuxFAQ/english.wav
六、应用

过去,Linux主要被用作服务器的操作系统,但因它的廉价、灵活性及Unix背景使得它很合适作更广泛的应用。传统上有以Linux为基础的“LAMP (Linux, Apache, MySQL, Perl/PHP/Python的组合)”经典技术组合,提供了包括操作系统、数据库、网站服务器、动态网页的一整套网站架设支持。而面向更大规模级别的领域中,如数据库中的Oracle、DB2、PostgreSQL,以及用于Apache的Tomcat JSP等都已经在Linux上有了很好的应用样本。除了已在开发者群体中广泛流行,它亦是现时提供网站务供应商最常使用的平台。

基于其低廉成本与高度可设定性,Linux常常被应用于嵌入式系统,例如机顶盒、移动电话及行动装置等。在移动电话上,Linux已经成为与Symbian OS、Windows Mobile系统并列的三大智能手机操作系统之一;而在移动装置上,则成为Windows CE与Palm OS外之另一个选择。目前流行的TiVo数位摄影机使用了经过客制化后的Linux。此外,有不少硬件式的网络防火墙及路由器,例如部份LinkSys的产品,其内部都是使用Linux来驱动、并采用了操作系统提供的防火墙及路由功能。

采用Linux的超级电脑亦愈来愈多,根据2005年11月号的TOP500超级电脑列表,现时世上最快速的两组超级电脑都是使用Linux作为其操作系统。而在表列的500套系统里,采用Linux为操作系统的,占了371组(即74.2%),其中的前十位者,有7组是使用Linux的。

2006年开始发售的SONY PlayStation 3亦使用Linux的操作系统。之前,Sony亦曾为他们的PlayStation 2推出过一套名为PS2 Linux的DIY组件。至于游戏开发商雅达利及id Software,都有为其旗下的游戏推出过Linux桌面版本。此外,Linux Game Publishing亦有专门为Linux平台撰写游戏,并致力于把其他在Windows平台编撰的游戏程序码转携至Linux平台,及为转携游戏提供使用授权。
而一个打算对所有生活在发展中国家孩子提供手提电脑的名为每孩子皆有一部手提电脑(OLPC)的项目,正是使用Linux作为缺省的操作系统。

七、发行版

Linux 主要作为Linux 发行版(通常被称为"distro")的一部分而使用。这些发行版由个人,松散组织的团队,以及商业机构和志愿者组织编写。它们通常包括了其他的系统软件和应用软件,以及一个用来简化系统初始安装的安装工具,和让软件安装升级的集成管理器。发行版为许多不同的目的而制作, 包括对不同计算机结构的支持, 对一个具体区域或语言的本地化,实时应用,和嵌入式系统,甚至许多版本故意地只加入免费软件。目前,超过三百个发行版被积极的开发,最普遍被使用的发行版有大约十二个.

一个典型的Linux发行版包括:Linux内核,一些GNU程序库和工具,命令行shell,图形界面的X Window系统和相应的桌面环境,如KDE或GNOME,并包含数千种从办公套件,编译器,文本编辑器到科学工具的应用软件。

目前最著名的发行版有Debian,红帽(redhat)、ubuntu、Suse、Mandriva(原Mandrake)、CentOS、fedora等

八、Linux与其他操作系统区别

Linux可以与MS-DOS、OS/2、Windows等其他操作系统共存于同一台机器上。它们均为操作系统,具有一些共性,但是互相之间各有特色,有所区别。

目前运行在PC机上的操作系统主要有Microsoft的MS-DOS、Windows、Windows NT、IBM的OS/2等。早期的PC机用户普遍使用MS-DOS,因为这种操作系统对机器的硬件配置要求不高,而随着计算机硬件技术的飞速发展,硬件设备价格越来越低,人们可以相对容易地提高计算机的硬件配置,于是开始使用Windows、Windows NT等具有图形界面的操作系统。Linux是新近被人们所关注的操作系统,它正在逐渐为PC机的用户所接受。那么,Linux与其他操作系统的主要区别是什么呢?下面从两个方面加以论述。

Linux与MS-DOS之间的区别

在同一系统上运行Linux和MS-DOS已很普遍,就发挥处理器功能来说,MS-DOS没有完全实现x86处理器的功能,而Linux完全在处理器保护模式下运行,并且开发了处理器的所有特性。Linux可以直接访问计算机内的所有可用内存,提供完整的Unix接口。而MS-DOS只支持部分Unix的接口。

就操作系统的功能来说,MS-DOS是单任务的操作系统,一旦用户运行了一个MS-DOS的应用程序,它就独占了系统的资源,用户不可能再同时运行其他应用程序。而Linux是多任务的操作系统,用户可以同时运行多个应用程序。

Linux与OS/2、Windows、Windows NT之间的区别

从发展的背景看,Linux与其他操作系统的区别是,Linux是从一个比较成熟的操作系统发展而来的,而其他操作系统,如Windows NT等,都是自成体系,无对应的相依托的操作系统。这一区别使得Linux的用户能大大地从Unix团体贡献中获利。因为Unix是世界上使用最普遍、发展最成熟的操作系统之一,它是七十年代中期发展起来的微机和巨型机的多任务系统,虽然有时接口比较混乱,并缺少相对集中的标准,但还是发展壮大成为了最广泛使用的操作系统之一。无论是Unix的作者还是Unix的用户,都认为只有Unix才是一个真正的操作系统,许多计算机系统(从个人计算机到超级计算机)都存在Unix版本,Unix的用户可以从很多方面得到支持和帮助。因此,Linux做为Unix的一个克隆,同样会得到相应的支持和帮助,直接拥有 Unix在用户中建立的牢固的地位。

从使用费用上看,Linux与其他操作系统的区别在于Linux是一种开放、免费的操作系统,而其他操作系统都是封闭的系统,需要有偿使用。这一区别使得我们能够不用花钱就能得到很多Linux的版本以及为其开发的应用软件。当我们访问Internet时,会发现几乎所有可用的自由软件都能够运行在 Linux系统上。有来自很多软件商的多种Unix实现,Unix的开发、发展商以开放系统的方式推动其标准化,但却没有一个公司来控制这种设计。因此,任何一个软件商(或开拓者)都能在某种Unix实现中实现这些标准。OS/2和Windows NT等操作系统是具有版权的产品,其接口和设计均由某一公司控制,而且只有这些公司才有权实现其设计,它们是在封闭的环境下发展的。

九、展望

一个打算对所有生活在发展中国家孩子提供手提电脑的名为每孩子皆有一部手提电脑(OLPC)的项目,正是使用Linux作为缺省的操作系统。
什么是Linux简单地说,Linux是一套免费使用和自由传播的类Unix操作系统,它主要用于基于Intel x86系列CPU的计算机上。这个系统是由全世界各地的成千上万的程序员设计和实现的。其目的是建立不受任何商品化软件的版权制约的、全世界都能自由使用的Unix兼容产品。
Linux的出现,最早开始于一位名叫Linus Torvalds的计算机业余爱好者,当时他是芬兰赫尔辛基大学的学生。他的目的是想设计一个代替Minix(是由一位名叫Andrew Tannebaum的计算机教授编写的一个操作系统示教程序)的操作系统,这个操作系统可用于386、486或奔腾处理器的个人计算机上,并且具有 Unix操作系统的全部功能,因而开始了Linux雏形的设计。
Linux以它的高效性和灵活性著称。它能够在PC计算机上实现全部的Unix特性,具有多任务、多用户的能力。Linux是在GNU公共许可权限下免费获得的,是一个符合POSIX标准的操作系统。Linux操作系统软件包不仅包括完整的Linux操作系统,而且还包括了文本编辑器、高级语言编译器等应用软件。它还包括带有多个窗口管理器的X-Windows图形用户界面,如同我们使用Windows NT一样,允许我们使用窗口、图标和菜单对系统进行操作。
Linux之所以受到广大计算机爱好者的喜爱,主要原因有两个,一是它属于自由软件,用户不用支付任何费用就可以获得它和它的源代码,并且可以根据自己的需要对它进行必要的修改,无偿对它使用,无约束地继续传播。另一个原因是,它具有Unix的全部功能,任何使用Unix操作系统或想要学习 Unix操作系统的人都可以从Linux中获益。