Just for Fun(5)

第三章 编程的美妙
1、 开始编程

我真不知道该如何阐释自己对编程的狂热,但我可以试试。

对于任何编程的人来说,编程是世界上最有趣的事。它比下棋之类的游戏更有乐趣得多,因为它可以由你自己来制订游戏规则。而你制定什
么样的规则,也就会导出与此规则相符合的结果。

然而,对于编程外行的人来说,它却似乎是世上最枯燥的事。

编程给人带来的最初兴奋的原因有部分是显而易见的,那就是:通过编程你可以支配一台计算机,你叫计算机做什么,它就做什么,永远准
确无误,而且毫无怨言。

这本身就很有意思。

但是计算机在一开始让你入迷的盲从性,显然不得它成为招人喜爱的伙伴。事实上,这种特性很快就让人厌烦了。真正使编程令人欲罢不能
的是:你能让电脑做你想做的事,但是你还必须想出怎样做到的办法。

我个人认为,计算机科学和物理科学有很多相似之处。两门学科都是在一个相当基础的层面上探讨一个体系是怎样运行的。当然,区别在
于,在物理学中,你探究的是一个已经存在的客观世界的构成。而在计算机科学中,你却是在创造一个前所未有的体系。

在电脑世界中,你就是创世者,你对所发生的一切拥有最终的控制。如果你功力深厚,你可以是上帝――在一个较小的层面上。

我这么说恐怕要得罪地球上近一半的人口了。但是的确如此。你开始创造自己的世界,而唯一限制你的就是机器的性能,以及――在今天尤
其如此――你自己的能力了。

想象一下建在树上的小屋的情形。

你可以建筑一个这样的房子,有一个活板门,既稳固又实用。但是每个人都可以看出一个仅仅以坚固实用为目的的树上小屋和一个巧妙地利
用树本身特点的美妙小屋之间的差异。这是一个将艺术和工程融为一体的活计。编程与造树上小屋有相似之外,这是它之所以被看成是一项
既有魅力同时又有实际贡献的活动的原因之一。在编程中,实用的考虑往往被置于有意思、美观伶俐或有震撼力的考虑之后。

编程是对创造的训练。

探究计算机工作原理的过程,是吸引我走进编程世界的最初原因。在这其中获得的最大的乐趣在于,我认识到了计算机科学与数学的类似:
你必须从该体系自身的规则出发,推演出整个世界,在物理科学中,你被客观规律所束缚。但是在数学和编程中,只要能合乎逻辑地推演,
就可以成立。思考数学问题,不会受到客观世界的逻辑的限制,数学只是逻辑自洽的符号体系。正如任何一名数学家都明白的,人完全可以
建构出一套数学等式,以证明三加三等于二。事实上,你想有什么样的体系就可以什么样的体系。但是,随着复杂程度的提高,你必须多加
小心,不要弄出什么与你创造的体系不一致的东西。

好的体系容不得任何错误。编程也与数学一样是这么回事儿。

人们对电脑如此着迷的原因之一,就是能从中获得自己创造一个新世界的体验,并贪图到它到底能够成为什么样子。在数学中,人们往往按
照客观事物的可能性进行思想实验。比如,说到几何时,大部分人想的是与我们的经验相符合的欧几里得几何学。但是电脑却可以帮助人们
形象化不同的几何,并不仅仅是欧几里得几何学。在电脑的帮助下,人们可以形象化这些虚构的世界,看到那些世界到底是什么样子。还记
得Mandelbrot set吗――基于Benoit Mandelbrot等式的fractal images。要不是电脑,纯粹的数学世界绝不能这样形象地展示出来。Mandelbrot就是人为
地制定了一些本不存在的与现实没有一点关系的世界规则,却创造出了的图案。通过编程和电脑,你能够构筑一个新世界,有时其设计会是
非常美妙的。

但是在大部分时间中你却不能欣赏自己创造的美妙世界。你只不过是在编写执行某一任务的程序。这时你就不是在创造一个新世界,而是在
电脑世界中解决一个具体的问题。问题通过将思考结果应用到问题中而得到解决。而能够坐下来,盯着电脑屏幕,将一个问题彻头彻尾地贯
穿思考,就需要某种特定的人。

比如,需要像我这种书呆子气十足的人。

操作系统是计算机的所有功能的基础。而创造一个操作系统则是最终的挑战。

创造操作系统,就是去创造一个所有应用程序赖以运行的基础环境――从根本上来说,就是在制定规则:什么可以接受,什么可以做,什么
不可以做。事实上,所有的程序都是在制定规则,只不过操作系统是在制定最根本的规则。

创造操作系统就像在为你创造的这片土地制订宪法,而其他在电脑上运行的程序则是为宪法所允许的普通法律。

有时,这些法律根本讲不能,但这正是你要面对的问题。你需要找到解决办法,并能够意识到自己以正确的方法找到了正确的答案。

还记得那些在课上总能答对问题的同学吗?他们的答案比别人来得快,他们能这样是因为他们没有刻意去追求。他们不在乎他们应该怎样来
答题。他们只不过找到了合理的考虑问题的方法。人们一听到正确答案,一切听起来就都是那么回事了。

在电脑上也是这样。你可以鲁莽、生硬地行事,愚蠢地死死纠住问题不放,直到问题不再成为问题。也可以通过找到正确的方法,使问题突
然消失。你可以从不同的角度看问题。直到灵光突现地认识到:问题之所以成为问题只在于你的方法错了。

也许能够证明这一点的最佳例子不是来源于计算机科学,而是数学。故事发生在十七世纪,日后将成为伟大的数学家的高斯还在上中学。一
天,老师厌烦了授课,就让学生们计算从1到100的数字的和。他原以为学生们要花一整天来计算这道题。没想到初展才华的数学家仅花了五分
钟就得出了正确答案:5050。他解答这个问题的方法不是真的把所有这些数字一一加起来,这样做既麻烦又愚蠢。他发现1加100等于101,2加99
也等于101,然后3加98还是101,直到50加51等于101。不要几秒种,他就注意到一共是50对101,所以答案是5050。

也许这故事是虚构的,但它想说明的道理却很清楚:一个伟大的数学家不会采用平庸而繁琐的方法,因为他能看到隐藏在问题背后的真正内
涵,并应用这一理解去找到更为简便的方法。

在计算机科学中也绝对如此。

没错,你能写一个程序来求出总数。这对于今天的电脑来说不过小事一桩。但是一个伟大的编程者能凭借其聪明的头脑就知道答案是什么。
他知道怎样写出漂亮的程序,知道怎样采用一种全新的但最终会被证明是正确的方法。

不过还是很难说清楚,闭门冥思苦想地要找到解决某个问题的漂亮答案,为什么竟然有如此巨大的魅力?但是,你要是曾经有过找到更好方
法的经历,你就会明白,这简直是无与伦比的感觉。

2、长腿的终端仿真器

我的终端仿真器(terminal emulator)长了腿。我经常用它登录到学校的电脑上,查阅电子邮件和参加MINIX讨论组。但是问题是,我还想下载
和上传东西。也就是说,我必须能向磁盘里保存东西。为此,我的终端仿真器必须装个磁盘驱动。还需要一个文件系统驱动,这样才能查看
磁盘里的东西,将我下载的内容保存成文件。

这是我在发明Linux的过程中差点半途而废的一步。我当时觉得这太麻烦,也不值得。但是除此之外我也没什么可做的。那年春天我在上课,
课程很简单,无需费太多的心思。当时我唯一的课外活动是每周三晚上参加同学聚会。我那时是一个社会“死物”(与“社会动物”相
对――译者),于是那聚会就成为我除了编程和学习之外唯一一个可以干点别的事情的场合。要不是那聚会,我可以说是彻底与世隔绝了。
同学聚会是我接触社会生活的仅有的地方,我几乎是每次不落地参加。该聚会对我是如此重要,以至于我有时会因为想着将要参加它而失
眠,因为一直担着心不要因为缺乏社交风度、或由于自己丑陋的大鼻子、或明显缺少个女伴而出丑。这自然都是些典型的低级趣味。我之所
以说这些,是因为要表明,当时我真的没有什么别的有意义的事情可做。而搞出驱动程序的工作却很有意思。所以我对自己说,我要干下
去。

于是,我写了一个磁盘驱动程序。因为我想把文件保存在我的MINIX文件系统中,也因为MINIX文件系统本身整理得很好,于是我让自己的文
件系统可以和MINIX文件系统兼容。这样,我就可以在MINIX系统下阅读我建立的文件并将它们存入同一张磁盘,以便MINIX系统也可以通过
我的终端仿真器阅读到我建立的文件。

这花费了我大量的精力:编程――睡觉――编程――睡觉――编程――吃饭(饼干)――编程――睡觉――编程――洗澡(冲冲了事)――
编程。随着工作的进展,这个项目很明显正在成长为一个操作系统。所以我转变了看法,不再把它看成一个终端仿真器,而是一个操作系
统。这个转变出现在我马拉松似的编程过程中的哪个时间段,是在白天还是晚上,我已经想不起来了。也许在这一刻之前我还穿着破旧的睡
袍奋力敲击着键盘,在为终端仿真器更多的功能;而转瞬之前我拥有的功能是如此之多,以至于整个体系已经变成了一个。

我把它称之为我的“gun-emac终端仿真程序”(gnu-emac of ternimal emulation programs)。gnu-emac终端仿真程序开始是一个编辑程序,但创造它
的人又为它增加了一系列功能。设计者本想让它成为一个可以用程序控制的编辑程序,但是其程序可控性的特点很快使一切都黯然失色,它
成为了一个从地狱中冒出来的编辑程序。它除了厨房水池子外几乎无所不包,这就是为什么这个编辑程序指令的图标有时竟是一个厨房水
池。这个编辑程序的一大特点就是,其设计过程比任何其他编辑器都更复杂。

我的终端仿真器也是如此。它在不断地扩张。

3、寻求网上帮助

来自:torvalds@klaava.Helsinki.Fi(李纳斯?托沃兹)

讨论组:comp.os.minix

主题:Gcc-1.40和一个有关POSIX的问题

信息名称:1991 Jul 3, 100050.9886@klaava.Helsinki.Fi

日期:1991年7月3日,格林威治时间10:00:50

各位网友好!

由于我现在正在MINIX系统下做一个项目,对POSIX标准很感兴趣。有谁能向我提供一个(最好)是机器可读形式的最新的POSIX规则?能有
FTP地址就更好了。

好吧,这就是一个芬兰小子希望检验一下自己的计算机技能限度的最早的公开证据。

POSIX标准是一个可以适用于数以百计的UNIX系统呼叫中的任意一个的一套冗长规则,计算机要执行任务(从读、写、开机和关机开始)就
需要这个标准。POSIX则是指一个UNIX的标准体系,或一个由来自不同公司的代表所组成的一个组织,希望按照一个共同的标准进行运作。
对于程序员开发的在该操作系统下的新应用软件或开发应用软件的新版本而言,标准是极其重要的。从POSIX这样的系统呼叫(system call),
尤其是重要的呼叫(call)中,我可以获得一个操作系统应该具有哪些功能的一个单子;然后我就可以通过自己的方式在自己的系统中实现每
一个功能。通过编写出这些标准,我的系统软件的源代码将可以被别人使用,以开发新的应用软件。

当时我并不知道我本可以直接从POSIX公司买到这些规则的软盘,但这无所谓。哪怕我能买得起,什么东西运到芬兰,往往会需要很长的时
间。我不愿等上那么久,因此我四处搜求一个能从FTP地址上直接下载的版本。

没有人给我提供能找到POSI标准的来源。于是我开始了计划B。

我从学校找到运行sun器(sun server)的sun微系统版的UNIX手册。该手册中有一个完全可以凑合使用的系统呼叫的基本版本。从用户手册中能看
出系统呼叫的主要功能,以及为完成这些功能所需要完成的步骤。但是,从中看不出具体的方法,而只是标明了最终的结果。于是我便着手
从安德鲁?塔南鲍姆的书中和别的材料中收集一些系统呼叫。最终有人给我寄来了那几卷厚厚的POSIX标准。

不过我发的那个邮件并没有石沉大海。任何一个有相应知识的人(只有具备相应知识的人才会上MINIX的网站)都能看出我的计划是要开发一个
操作系统,否则,我会需要POSIX规则呢?我的邮件引起了赫尔辛基工学院(我若不是对研究理论这么感兴趣,可能会在这儿求学)一个助教阿
里?莱姆克(Ari Lemke)的好奇。阿里善意地给我回信说,他愿意为我在他们学校的FTP地址上建一个子目录,这样到时我可以把自己的操作系统
发布上去,让感兴趣的人们下载。

4、Linux

阿里?莱姆克一定是一个相当乐观的人。在我能拿出什么可以发布的东西之前 ,他就为我建立了一个子目录:ftp.funel.fi。我有了密码,一切都
准备就绪,就等着我去登录然后上传内容了。但是我要再花上四个月才能找到一点我愿与世人分离的东西,或者至少与阿里或几个与我保持
邮件往来的热衷于操作系统的狂热分子分离的东西。

我最初的目录是想开发出一个最终可以取代MINIX的操作系统。

这个系统不必比MINIX能干,但必须能胜任我最喜欢用MINIX做的事,以及其他我想做的事。比如,MINIX的终端仿真不仅太不方便,而且也
不能进行任务控制――即把暂不用的程序放入背景中,同时内存管理也太简化。顺便说一下,它还是以苹果的操作系统(Mac OS)而不是以DOS
为支持的。

开发操作系统就是搞明白系统呼叫应该做什么,然后以你自己的方式编出能使系统呼叫得以执行的程序。总有来说,共有几百个系统呼叫。
有些是多功能的,其余的则很单一。有些更基本的系统呼叫确实是十分复杂的,并需要有大量的基础作为支持。比如,为完成“写”和“
读”这两种系统呼叫,你就必须建立一个磁盘驱动程序,以便能够在磁盘里读或写东西。又比如“打开文件”的系统呼叫,你就必须创建一
整套文件系统层,以便分析文件名和在磁盘上查找文件。要编写“找开文件”的系统呼叫,更需要几个月的工作。但这个程序一旦编写出
来,用于别的功能的程序都可以借鉴。

早期的创建工作就是这样。我不但从Sun服务器的操作系统手册中查找标准,也从其他书中查到可用的标准,挑出一个一个的系统呼叫,然后
为它们再编出可行的软件。

这工作很容易让人感到灰心丧气。

原因是:表面上一切如故,你看不到任何进展。你可以做几个试验性的程序,检验自己刚刚加上的东西是否可行。但这并不真的有什么用。
在有的阶段你不得不放弃刚才的想法,那一长串的系统呼叫都白干了。一个真正的程序在运行之前,必须已经接近完成。你必须首先运行的
程序是外壳(shell)程序,而在有这个外壳程序之前,要运行什么很难的。而且,这个外壳程序包括了很多你所需的系统呼叫。它运行起来之
后,你才会从中找到一长串你尚需完成的功能。

在UNIX中,外壳程序是一切程序之母。它的作用是引发以后的一系列二进制语言程序(二进制程序是以计算机可识别的1和0为符号编写的程
序。以机器语言编程,就是将二进制的源代码组合起来),这个外壳程序使你首先能登录(当然在真正的UNIX系统中,你运行的第一个程序传
统上被称为init,而init的确需要很多基础的支持才能运行。它是一种对正在运行着的程序的控制工具。当没有任何程序运行时,init就没有用
了)。

因此,我做的第一件事不是创建init,而是建一个外壳程序。我执行了约二十五个系统呼叫,正如我所说,这也是我要运行的第一个真正的程
序。这个外壳程序不是我自己编的。我下载了一个叫Borne Shell的外壳程序。它是UNIX的初始外壳程序之一,可以从互联网上免费下载,名字
来源于一个难听的双关语。编写该外壳程序的家伙名叫波恩(英文中的“忍受”或“出生”之意――译注),所以这个程序就叫做“Borne Again
Shell”(“再次忍受或再次降生外壳程序”――译注),或一般被称为BASH(bash的英文意为“重击”――译注)。

当你试着从磁盘运行或导入一个真实的程序时,一般都会有“臭虫”(bug,IT业中称软件里的瑕疵为“臭虫”――译注)出现在磁盘驱动程序
或导引程序中,因为程序往往会不理解它读到的东西,于是它就会在屏幕上显示出相关的信息。这很重要,你可以从中知道哪儿出了毛病。

于是,我就到了这样一个阶段:我试图导引外壳程序,屏幕上则显示出外壳程序中每一个我尚未能执行的系统呼叫。我导入并运行外壳程
序,屏幕上则出现类似“系统呼叫517没有执行”这样的信息。我日日夜夜盯着屏幕上显示出来的系统呼叫,试着发现我在哪一条上面出了
错。这比拿到一个系统呼叫的单子,然后一一使他们可以被执行要有意思多了。人们需要看到事情的进展。

到了八月底或九月初的时候,我的外壳程序终于可以工作了。过了这一关,后面的任务就轻松多了。

这可是一件大事。

我的外壳程序可以运行后,我马上开始着手其他几个程序,比如拷贝程序和列表程序,这些都比外壳程序简单得多。你所需的一发,外壳程
序早就具备好了,所以一旦外壳程序完成,就好像是从0飞跃到100一样,因为一切都已就绪。这时,我已经拥有了一切必要的条件,感觉就像
上帝创世纪那样,执掌一切地说:“让那里有光”,那里就真的有了光。在此之前,的确是一无所有。

是的,我深感满意。

这种满意很重要,因为那个夏天我除了伏在电脑面前,其他什么都没做。这么说一点也不夸张。芬兰四月到八月的日子是一年中最美好的时
光。人们到布满小岛的海上航船,去海滩上晒日光浴,到夏日小木屋中消闲。但是我却在没日没夜地工作,不知哪一天是周末,哪一天是工
作日。学生的黑色窗帘遮蔽了几乎昼夜灿烂的阳光,也遮蔽了整修世界。有些天――或夜晚?――我会从床上爬起来后直接坐到离床仅几英
尺远的电脑旁。

爸爸显然在不断催促妈妈让我在暑假找份工作,但妈妈却不在乎:我并没有打搅她。萨拉会因为我有时上网使电话战线而有点恼火。她也许
会写些毫不客气的话。毫不夸张地说,我和电脑之外的世界几乎没有任何联系。

当然,也许每周有一次,一个朋友会敲敲我的窗户,而我如果没在捣腾什么重要的编码时,会请他进来。我们会喝杯茶,也许还会挤在窄小
的厨房里看一小时的MTV。现在回想起来,对了,有时乔科会来敲我的窗户,我们会出去喝点啤酒或玩司诺克台球。但是,诚实地说,那时
我的生活也就这么简单了。

而我一点儿都不感觉自己是那种面色苍白、可怜兮兮的失败者。

外壳程序成功了。这意味着,我事实上已经建立起了可行的操作系统的基础,而我自己则乐趣无穷。

外壳程序成功之后,我开始检验其中的内装程序。接着我又编了足够的新程序,可以真正干点什么了。我用了MINIX中所有有用的东西。当我
把外壳程序移到一个我为新的操作系统所建的特别区域中时,我开始把这个操作系统称为“Linux”。

坦率地说,我一开始并不想把它以Linux的名称发布出去,因为那显得我太自我中心了。那么,我为最终发布起的名字是什么呢?Freax (Freaks
的变形,该词为“异想天开”之意――译注。) 事实上,在一些早期完成的文件中,即那些说明如何汇编源代码的文件中,有将近半年的时间
我一直使用Freax这个名称来指代这个操作系统。但这其实是无所谓的,因为当时还没有任何人知道它,所以它其实并不需要什么名字。

5、开放源代码

来自:李纳斯托沃兹torvalds@klaava.Helsinki.Fi (李纳斯?托沃兹)

新闻组:comp.os.MINIX

题目:你在MINIX中最想看到什么?

总结:关于我的新操作系统的小型民意调查。

信息编号:1991年8月25,9541@klaava.Helsinki.Fi

嗨,所有使用MINIX的人们,大家好!

我在编一个(免费的)用于386(486) AT clones的操作系统(只是一个爱好,不会成为一个像Gnu那样大型的专业软件)。我从四月起就在酝酿,现在
已准备就绪。我想听一听人们对MINIX有哪些欣赏或不满之处,因为我的操作系统和它有些相象(尤其是文件系统的物理排列方式)。

我目前已经装上了bash (1.08)和gcc (1.40),看来一切进展顺利,估计几个月内我就会得出一些实用的东西。我想知道大多数人在这方面有什么
要求。任何建议都欢迎,但我可不保证一定会采纳。

李纳斯(torvalds@klaava.Helsinki.Fi)

附:对了,它不受任何MINIX源代码的影响,并有一个多线程的FS。记住。它不能安装(比如使用386任务转换文件等等),也许永远不会支持除
了AT硬盘之外的东西。情况就是这样。

使用MINIX人群中最坚定的操作系统的狂热者看到了火花。我没有收到多少有关MINIX特点的建议,但却有许多别的问题。

>多告诉我们一点!它需要MMU吗?

回答:是的。

>在多大程度上使用C语言?在装截中会有什么困难?不会有人相信你的不可“半截性”,比如说我就想把它装截到我的Amiga软件上。

回答:它大部分使用C语言,但大部分人不会把我写的程序称作C语言。它使用了我能想到并找到的386的特征,我也想通过它充分了解386。
我的一些C文件几乎和C语言一样是组合起来的。

如上所述,它也使用MMU,用于分页和分类(还不能存进软盘里)。正是分类使它成为一个真正的386的依赖者(每项任务都有一个64mb的编码和
数据分类文件。

甚至有几个人提出愿意做试用版的试验者。

最后,把它发布出去并不需要太大的决心。我一直习惯与人交流程序,所以要做的唯一真正决定就是,我敢于向人们展示这一系统软件的最
佳时机是何时,才不会使自己感到不自在,或更确切地说,什么时候发布才使我将来不至于为此感到羞愧。

我最终想实现的是有一个编译器和一个能在Linux内部编程的真正的系统环境,而不必再用MINIX。但是当gnu程序可以运行时,我骄傲极了,
愿意让整个世界都看到它。同时,我也想听到人们的反响。

外壳程序能运行时,我已为操作系统初步编了几个程序。其实还不能做什么,但你能看出和UNIX很相似。事实上,它运行起来像一个有点残
疾的UNIX。