RMS 在瑞典皇家理工学院(KTH)的讲座,1986年
本文是 Richard Stallman 1986年10月30日在瑞典斯德哥尔摩 Kungliga Tekniska Högskolan(皇家理工学院)讲座的文字稿。学生社团 Datorföreningen Stacken 组织了此次活动。
注:本文字稿对讲座的内容略有修改。例如,讲座的开头有个口误,还有一些口头常见但书面少见的表达方式。如果不对讲座做修改,很难做出正确且清晰的书面英语表达。
Rms:看来人们希望我讲三个方面的事情。一方面,我理解面对黑客最好的事情是讲一讲 MIT 的岁月是什么样的,其人工智能实验室有什么特别。不过,我还听说现在这批人和周一及周二来参会的人完全不同,而我应该讲一讲 GNU 工程的进展以及为什么软件和信息不应被独占。这意味着三个演讲,由于其中两个每个都差不多一个小时,所以我们会花比较长的时间。我想我可能会把演讲分成三个部分。如果你对某个部分不感兴趣,你可以离开;当某部分到结尾时,我会提示大家可以自由离开,同时让 Jan Rynning 到外面请其他人进场。
[有人说:“Janne, han trenger ingen mike.”(翻译:“Janne,他不需要麦克风。”)]
Jan,你准备好去场外召集其他人进场了吗?
Jmr:我在找麦克风,据说它锁在柜子里。
Rms:在人工智能实验室时,我们会用大锤砸开门;而破门而入则是对胆敢把公用设备锁起来的人的教训。不过,幸运的是,我学过保加利亚歌唱,所以不用麦克风也没问题。
不管怎样,我是需要提示你们演讲的各个部分,还是你们想要全程在场?[回应:是的]
我开始编程是在1969年,那时我在纽约的一家 IBM 实验室。之后,我去了一所设有计算机科学系的学校,它可能和大部分此类学校一样。几个教授主管要做什么事,还有一些人管理谁可以使用什么。那时,大多数人都缺少终端,但是许多教授在自己的办公室里有终端,简直是浪费,但这就是他们的一贯作风。当我到人工智能实验室时,我发现这里的作风和那里完全不同。例如,这里的终端被认为是属于所有人的,如果有教授把终端锁在办公室里,那么他们就要体会门被砸开的无奈。我确实看到过一个装着大铁块的小车,那就是用来砸开一个胆敢把终端锁住的教授办公室大门的工具。那时侯,终端太少,整个系统大概有5个显示器终端,所以有一个被锁就是了不得的损失。
此后经年,我受到此理念的鼓舞,曾经多次爬上屋顶或钻入地下室去打开紧锁设备的大门让人使用,通常我会留言给人们解释为什么锁门是自私的行为。锁门的人大概只考虑了自己的利益。他们当然有锁门的缘由,可能是防止东西丢失才想锁门,但是他们没能为会被锁门影响的其他人着想。我曾经提醒他们,几乎每次锁门,他们都不能自己决定是否应该锁门,他们都可以找到一个折中的方案:把他们担心的东西放倒另一个地方,比如可以锁起来的柜子,或者另外一个房间。但是人们就是不愿意去采取这些方案。他们认为:“这是我的房间,我可以上锁,其他人见鬼去吧,”而这种态度正是我们教育人们要摈弃的态度。
但是这种破门而入的作风不是一个孤立的事件,它是一种完整生活方式的一部分。人工智能实验室的黑客非常热衷于编写优秀和有趣的程序。正是因为他们热衷于完成更多的工作,他们才不能忍受锁起来的终端,也不能容忍许多其他会阻碍他们工作的事。这就是对工作有真正热情的人和仅仅只是为了工作而已的人的区别。如果仅仅只是为了工作而已,人们不会关心雇主的时间和金钱,如果雇主蠢到让他们干坐着,那么他们就会干坐着不做事。在这种地方毫无乐趣。
另外,我们在人工智能实验室没有文件保护。那里的计算机完全没有安全设施。我们非常自知地选择了这种方式。编写不兼容分时系统的黑客认为文件保护通常是为自以为是的系统管理员使用的,他们想要拥有超越其他人的权力。因为黑客们不想让任何人有这样的权力,所以他们没有实现这样的功能。其结果是,每当系统出问题时,你总能修复之。你不需要坐以待毙,也不必因为有人不相信你而无权修复,因为你准确地知道哪里出问题了。当你很自信地知道该如何解决问题时,你就无需放弃工作而回家、等着第二天早上有人来解决问题。
我们也未曾让教授或老板决定该做什么,因为我们的工作是改进系统!当然,我们会和用户交谈;如果不这样,那么你就不知道该做什么。此后,我们就是最能看清什么样的改进是可行的人,而且我们总是互相讨论如何改进系统以及如何借鉴其他系统的优点。其结果就是,我们有了一个顺畅的执行架构;自从有了这些经历,我就确信这是人们最好的生活方式。
遗憾的是,人工智能实验室的这种形式被摧毁了。多年来,我们一直担心人工智能实验室会被另一个麻省理工学院的实验室摧毁,就是计算机科学实验室——其主管是帝国建造者的类型,为了在学院升职和扩大自己的组织,他可以做任何事,而且他一直企图把人工智能实验室变成自己实验室的一部分。因为他觉得人们应该服从命令之类的事,所以没人愿意按照他的方式做事。
但是我们设法战胜了这个危险,我们只是被我们从未想到的敌人摧毁了——商业主义。到80年代早期,黑客们突然发现自己所做之事变得有利可图——到私有企业工作可能致富。他们只需停止分享自己的工作并摧毁人工智能实验室,尽管我尽了最大努力来阻止他们,他们还是做了。
基本上除我以外,人工智能实验室的优秀程序员都被挖走了,其后果不是暂时的变化,而是永久的转型,因为这打断了黑客文化的传承。新黑客总是被老黑客所吸引;那里有最好玩的计算机,那里的人做着最有趣的事情,而且那里的氛围也令人向往。一旦没有了这些元素,那里就失去了向新人推荐的理由,新人就不会来了。那里没有了让人敬仰的高手,没有了可以学习传统的前辈,更没有了学习成为优秀程序员的楷模。只有一些教授和不知程序所以然的研究生,你无法学习如何做出优秀的程序。因此,我热爱的麻省理工学院人工智能实验室一去不复返。我尝试用几年的抗争来惩罚那些造成此局面的人,后来我决定我应该投身于创建一个传承着实验室精神的新社区。
但我必须面对的一个问题是 专有软件。例如,实验室里发生的一件事是,黑客们离开后,我们的机器和开发的软件再也无法维护了。软件当然可以运行,如果没人改动它,它就能继续运行,但机器不行。机器会出故障,却没人能修理,最终它们只能被扔掉。过去,我们确实为机器签订了维修合同,但这基本上是个笑话。那只是人工智能实验室的专家黑客们解决问题后获取零件的一种方式。因为如果让现场维修人员来修理,他们要花好几天时间,而你不想那样,你希望机器能正常工作。所以,那些懂行的人会直接快速修好,由于他们的能力比任何现场维修人员高出十倍,他们能做得更好。然后他们会把损坏的板子留在那里,告诉现场维修人员:“把这些拿回去,给我们换些新的。”
在过去,我们黑客经常改装从 DEC 公司买来的机器。例如,他们为 PDP-10计算机制作了分页装置。如今,我想这里(斯德哥尔摩)也有人做类似的事情,但在当时,这可是相当罕见的。而在更早的时候,也就是20世纪60年代初,人们常常改装计算机,添加各种新指令和新奇的分时特性。因此,到了70年代中期 MIT 的 PDP-1退役时,它的指令数量比60年代初刚从 DEC 公司交付时多了大约一倍,还配备了特殊的硬件调度辅助功能、奇特的内存映射特性,使得可以将单个硬件设备分配给特定的分时任务,以及许多我都不太了解的功能。我想他们还内置了某种扩展寻址模式,添加了索引寄存器和间接寻址,基本上把一台弱小的机器改造成了还算说得过去的机器。
我猜,超大规模集成电路的缺点之一就是,给机器添加指令已经不再那么可行了。
PDP-1还有一个非常有趣的特点,那就是你可以用很少的指令编写出有趣的程序。比此后任何其他机器都少。例如,我相信那个著名的显示奇技“咀嚼方块”——它让方块变大,然后分裂成许多更小的方块,这些方块再变大,再分裂成更小的方块——在 PDP-1上大概只用五条指令就写出来了。还有许多其他漂亮的显示程序也可以用很少的指令编写出来。
这就是人工智能实验室的情况。那么,除了无政府主义倾向,黑客们的文化又是怎样的呢?在 PDP-1的时代,最初,一次只能有一个人使用机器。几年后,他们编写了一个分时系统,并为此增加了大量硬件。但一开始,你只能预约一段时间上机。当然,教授和从事官方项目的学生总是在白天使用机器。所以,那些想获得大量机时的人就会预约竞争较少的夜晚时间,这就形成了黑客们夜间工作的习惯。即使有了分时系统,晚上获得机时仍然更容易,因为用户较少,你能得到更多的计算周期。因此,想完成大量工作的人,依然会选择在夜晚到来。但到那时,情况开始有所不同,因为你不再是一个人,还有其他几位黑客也在,这就成了一种社交现象。白天你来实验室,会遇到那些并不真正热爱机器的教授和学生;而如果你晚上来,遇到的则都是黑客。于是,黑客们选择在夜晚聚集,融入他们的文化圈子。他们还发展出了其他传统,比如凌晨三点一起去吃中餐。我记得许多次日出,都是在从唐人街回来的车上看到的。看日出其实是一件非常美妙的事,因为那是一天中最宁静的时刻。那是一个准备上床睡觉的绝妙时辰。走在回家的路上,天色渐亮,鸟儿开始啁啾,这种感觉真好,你能从当晚完成的工作中获得一种宁静的满足感和平和的心境。
我们开始的另一个传统是在实验室里睡觉。自从我第一次到那里,实验室里就至少放着一张床。我可能比大多数人在实验室里待的时间更长些,因为每隔一两年,由于这样或那样的原因,我总会有一段时间没公寓住,就会在实验室里住上几个月。我一直觉得那里很舒服,夏天也凉爽宜人。但是,看到有人在实验室里睡着也一点儿都不稀奇,这同样是因为他们的热情:你尽可能长时间地熬夜编程,因为你根本不想停下来。等你完全精疲力竭了,就爬到附近最近的一块柔软的平面上去睡。那里的氛围非常随意。
但当黑客们纷纷离开实验室后,这就造成了人员结构的变化。因为那些并不真正热爱机器的教授和学生们人数和以前一样多,所以现在他们成了主导群体,而且他们非常害怕。没有了黑客来维护系统,他们说:“我们将会面临一场灾难,我们必须使用商业软件。”他们还表示:“我们可以指望公司来维护它。”事实最终证明他们完全错了,但他们当时就是这么做的。
那恰好是在一套新的 KL-10 系统即将到来的时候,问题就在于:它是要运行 ITS(不兼容分时系统),还是运行 DEC 的 Twenex 系统。那些可能会支持使用 ITS 的黑客们已经走了,于是学院派们选择了运行商业软件,这随即产生了几个直接后果。其中一些后果并非立竿见影,但任何稍加思考的人都能看出,它们是必然会随之而来的。
其一,那个商业软件编写得更糟糕,也更难理解;因此人们更难对其进行实际所需的修改。其二,那个软件带有安全机制,这不可避免地导致了人们之间的合作减少。在过去使用 ITS 的日子里,我们认为每个人都能查看、修改任何文件是可取的,因为我们有理由这样做。我记得有一个有趣的“丑闻”:有人在使用 Macsyma 时发出了求助请求。Macsyma 是 MIT 开发的一个符号代数程序。他向其中一个开发人员求助,几个小时后,他却收到了另一个人的回复。他吓坏了,发消息说:“某某肯定在看你的邮件,难道你们的系统上邮件文件没有受到适当的保护吗?”“当然,我们系统上没有文件是受保护的。有什么问题吗?你更快得到了答复,为什么不高兴呢?我们当然会互相看邮件,这样才能找到像你这样的人并提供帮助。”有些人真是身在福中不知福啊。
当然,Twenex 不仅自带安全机制,而且默认开启,它的设计本身就假设用户会启用安全功能。因此,有很多非常容易操作的事情可能会造成巨大的破坏,而唯一能阻止你意外造成破坏的,就是安全机制。在 ITS 上,我们发展出了其他方法来防止人们无意中做出这些事,但在 Twenex 上,这些机制就不存在了,因为它的设计假设是严格的安全措施会一直生效,只有管理员才有权执行这些操作。所以他们没有加入任何其他机制来防止意外操作。结果就是,你无法简单地关闭 Twenex 的安全功能而得到一个真正想要的系统,而且当时也没有黑客能进行修改,加入那些替代机制,所以人们被迫使用安全功能。大约在那台机器安装六个月后,就开始发生一些“政变”了。也就是说,起初我们都假设,为实验室工作的每个人都会拥有“wheel 权限”,可以凌驾于所有安全措施之上。但后来,某天下午你来上班时,可能会发现几乎所有同事的 wheel 权限都被关闭了。
当我发现这些情况后,我推翻了他们。第一次,我碰巧知道某个属于特权阶层的人的密码,于是就用那个密码把所有人的权限都恢复了。第二次,那个人改了密码,他的立场也变了,成了贵族派的一员。所以,我不得不让机器停机,使用非分时模式的 DDT 来探查。我在监控程序里摸索了一阵子,最终弄清楚了如何让它加载自身并让我打补丁,这样我就能关闭密码验证,然后重新打开了大量人员的 wheel 权限,并发布了一条系统消息。我得说明一下,这台机器名叫 OZ,所以我发的系统消息是:“又一次夺权企图。到目前为止,贵族势力已被击败——自由 OZ 广播电台。”后来我才发现,“自由 OZ 电台”是火炉剧院1 用过的一个梗。我当时并不知道。
但渐渐地,情况变得越来越糟,这正是系统构建方式本身的特性所决定的,它迫使人们要求越来越多的安全性。直到最后,我被迫停止使用那台机器,因为我拒绝使用保密的密码。自从密码首次出现在 MIT 人工智能实验室,我就得出结论:为了坚持我的信念,遵循我认为不应该存在密码的理念,我应该始终确保使用一个尽可能明显的密码,并且告诉每个人它是什么。因为我不认为在计算机上设置安全措施是可取的,所以我也不应该有意愿帮助维护这种安全体制。在允许的系统上,我使用“空密码”;在不允许这样做的系统上,或者空密码意味着你无法从其他地方登录等情况下,我就用我的登录名作为密码。这已经是最明显的了。当有人指出这样别人可能会以我的身份登录时,我说:“没错,就是要这样,也许有人需要从这台机器上获取某些数据。我想确保他们不会被安全机制挡在门外。”
另一件我始终坚持的事是,我总是关闭自己目录和文件的所有保护。因为我的目录里时不时会存着一些有用的程序,如果程序出了故障,我希望人们能够修复它。
而且,那台机器在设计上也没有支持一种叫作“游客”的现象。“游客”是人工智能实验室一个非常悠久的传统,与我们其他形式的无政府主义相伴而生,那就是让外面的人来使用我们的机器。在以前,任何人都可以走到机器前,随意以任何身份登录,这自然而然就实现了:如果你来访,就能登录并使用机器。后来,我们把它稍微正规化了一点,将其作为一个公认的传统,尤其是在 Arpanet2 出现后、人们开始从全国各地连接到我们的机器之后。我们所期望的是,这些人能够真正学会编程,并开始修改操作系统。如果你对其他地方的系统管理员说这话,他肯定会吓坏。如果你建议让任何外部人员使用机器,他会说:“那万一他开始修改我们的系统程序怎么办?”但对我们来说,当一个外部人员开始修改系统程序时,这意味着他表现出真正的兴趣,想要成为社区中有贡献的一员。我们总是鼓励他们这么做。当然,从编写新的、小的系统工具开始,我们会检查他们所做的工作并加以指正,然后他们就可以继续为现有的、大型的工具添加功能。这些程序已经存在了十年甚至十五年,由一位又一位工匠不断添加新功能,一点一点地成长壮大。
有点像你可以说,就像法国的那些城市,在那里你能看到极其古老的建筑,上面叠加着几百年后直到如今各个时代的增建部分。而在计算领域,一个始于1965年的程序本质上就是如此。所以我们总是希望“游客”们能成为系统维护者,也许在他们已经开始参与系统程序工作并向我们展示了他们有能力做出优秀成果之后,他们就会被聘用。
但 ITS 机器还有其他一些特性有助于防止这种情况失控,其中之一就是“间谍”功能,任何人都可以查看其他人在做什么。当然,游客们喜欢这个间谍功能,他们觉得这很巧妙,有点小刺激,你懂的。但结果是,如果有游客开始做任何可能惹麻烦的事,总会有别人在看着他。所以很快他的朋友们就会非常生气,因为他们知道,“游客”传统能否延续取决于游客们是否负责任。因此,通常总会有人认出那个捣乱的人是谁,然后我们就能让他不再打扰我们。如果我们做不到,那么我们会采取的措施就是,暂时完全关闭来自某些地方的访问权限,等我们重新开放时,那个捣乱的人可能已经离开,把我们忘了。就这样,年复一年,一直如此。
但 Twenex 系统并不是为这种事情设计的,最终他们无法容忍我使用那个众所周知的密码,游客们总是两三个人同时以我的身份登录,所以他们开始清除我的账户。而那时我主要也在其他机器上工作,所以最终我放弃了,再也没登录过那台机器。就这样了。我再也没有以我自己的身份登录过那台机器[此时,RMS 的演讲被雷鸣般的掌声打断。]……
但当他们最初拿到这个 Twenex 系统时,心里有几个想要修改的地方。他们想改变安全机制的工作方式。他们还想让这台机器同时接入 ARPA 网络和 MIT-chaos 网络,但结果他们发现做不到,因为他们找不到足够有能力的人来进行这样的修改。已经没有这样的人才去做了,而且系统本身也太难改动。那个系统更难理解,因为它编写得太糟糕了,当然,DEC 公司也不会去做这些事。所以,他们原本以为商业系统基本上可以自我维护的想法,被证明是错误的。他们同样迫切需要系统黑客,却再也没有办法吸引系统黑客了。而如今在 MIT,对钻研 ITS 感兴趣的人,比对钻研 Twenex 感兴趣的人还要多。
而最终导致这种局面的原因是 Twenex 无法被共享。Twenex 是一个专有程序,你只有在以某种不光彩的方式保密源代码的条件下才能拥有它,这本身就让人反感。除非一个人对此浑然不觉(计算机领域确实有些人是这样的,有些人只要觉得好玩什么都干,根本不会去思考自己是否在与他人合作,但如果你要编写那样的程序却意识不到这是一件多么可悲的事情,那你得迟钝到什么程度啊?这本身就进一步打消了人们的积极性)。如果这还不够,还有一点:每隔一年左右,他们就会给你发布一个新版本,里面充斥着五万行由“猴子”新写的代码。因为他们基本遵循的是“一百万只猴子敲键盘,最终总能敲出点有用的东西”这套系统开发理念。
从我在这些专有系统上目睹的一切,让我清楚地认识到,要重拾旧日人工智能实验室的精神,唯一的方法就是拥有一个自由的操作系统。一个由自由软件构成、可以与任何人分享的系统。这样我们才能邀请所有人一起来改进它。而这正是促成 GNU 工程的原因。那么,我想我们就进入演讲的第二部分了。
大约三年半以前,我很清楚地意识到,我应该着手开发一个 自由软件 系统。当时我能看到两种可以开发的系统类型:一种是类似 LISP 机的系统,本质上就像刚刚开发出来的 MIT LISP 机器系统 [1],但它是自由的,并且运行在通用硬件上,而不是专用的 LISP 机器上。另一种可能性是更传统的操作系统,我很清楚,如果我做一个传统的操作系统,我应该让它与 Unix 兼容,因为这样世界各地的人们就更容易切换过来。没过多久,我决定选择后者,原因是我意识到,在通用硬件上你无法真正拥有像 LISP 机器系统那样的东西。LISP 机器系统使用专用硬件加上可写的微码,以实现良好的执行速度和运行时错误的稳健检测,特别是数据类型错误。为了让 LISP 系统在普通硬件上运行得足够快,你不得不开始做一些假设。假设某个参数的类型是正确的,然后如果不是,系统就会直接崩溃。
当然,你可以加入显式的检查,如果你想的话,也能写出健壮的程序。但事实是,如果你没有加入检查机制,当向函数传入错误类型的参数时,就会出现内存寻址错误之类的问题。
所以结果就是,你需要有一个在 LISP 系统底层运行的东西来捕获这些错误,并让用户能够继续运行,调试他所遇到的问题。最终我得出结论:如果我必须有一个更底层的操作系统,那我干脆做一个优秀的操作系统——这成了一个选择:是做一个带 LISP 的操作系统,还是只做一个操作系统;因此我应该先做操作系统,并且要让它与 Unix 兼容。最后,当我意识到可以用英语中最有趣的一个词来命名这个系统时,该做何选择就再清楚不过了。这个词当然就是 GNU,它代表 “Gnu's Not Unix”(GNU 并非 Unix)。这种递归缩写是麻省理工学院黑客社区一个非常悠久的传统。据我所知,它始于一个名为 TINT 的编辑器,意思是 “Tint Is Not Teco”(TINT 并非 Teco);后来又有像 SINE(“Sine Is Not Emacs”,SINE 并非 Emacs)、FINE(“Fine Is Not Emacs”,FINE 并非 Emacs)、EINE(“Eine Is Not Emacs”,EINE 并非 Emacs)、ZWEI(“Zwei Was Eine Initially”,ZWEI 最初是 EINE)这样的名字;最终,现在有了 GNU。
我想说,从大约两年半前我真正开始着手开发 GNU 算起,我已经完成了超过一半的工作。在我准备启动这个项目时,我首先开始四处寻找已经存在的可用自由软件。我发现了一个有趣的便携式编译器系统,叫做“自由大学编译器套件”,我心想,名字都这么叫了,也许我可以用它。于是,我给开发这个系统的人发了消息,问他是否愿意把它提供给 GNU 项目,他说:“不行,大学可能是自由的,但他们开发的软件不是。”但他接着又说,他也想要一个 Unix 兼容的系统,并且想为它写一个内核,所以他建议,不如我来编写各种工具软件,然后它们可以和他的专有编译器一起发布,以此来鼓励人们购买他的编译器。我觉得这种做法很可鄙,于是告诉他,我的第一个项目将是一个编译器。
我当时对优化编译器了解得并不多,因为我从没做过这方面的工作。但我确实搞到了一个编译器,当时有人告诉我它是自由的。那是一个叫 PASTEL 的编译器,作者说这个名字的意思是“不地道的 PASCAL”。
Pastel
是一种非常复杂的语言,包含了许多特性,比如参数化类型、显式类型参数,以及许多复杂的东西。这个编译器本身当然是用这种语言编写的,并且具有许多复杂的特性来优化对这些东西的使用。例如:该语言中的
“string”(字符串)类型就是一种参数化类型;如果你想要特定长度的字符串,可以写 string(n);你也可以只写
string,那么参数就会根据上下文来确定。字符串非常重要,许多使用字符串的构造都需要快速运行,这意味着他们必须加入大量特性来检测诸如:当字符串的声明长度是一个在整个函数中已知为常量的参数时,要保存这个值并优化它们将要生成的代码,诸如此类许多复杂的事情。但是,从这个编译器中,我确实学到了如何进行自动寄存器分配,以及一些关于如何处理不同种类机器的思路。
嗯,由于这个编译器已经能编译 PASTEL 语言了,我需要做的就是为它增加一个 C
语言的前端(这个我做了),以及为68000处理器增加一个后端(我预计这将是我的首个目标平台)。但我遇到了一个严重的问题。因为 PASTEL
语言的定义不要求在使用前声明,声明和使用可以任意顺序,换句话说:Pascal 里的 forward
声明在这里是多余的。正因为如此,有必要读入整个程序,保存在内存中,然后一次性处理完。结果是,编译器使用的中间存储,所需内存的大小与你的文件大小成正比。还要包括栈空间,你需要巨大的栈空间,而我发现的结果是:我所能使用的68000系统无法运行这个编译器。因为那是一个糟糕的
Unix 版本,给你的栈空间限制大概是16K 字左右,尽管机器有6兆字节的内存,你却只能有16K
字的栈或类似限制。当然,为了生成冲突矩阵,以查看哪些临时值相互冲突,或者哪些值与哪些值同时存活,它需要一个二次的位矩阵,对于大的函数来说,这会占用几十万字节。于是,我设法调试了这个编译器大约十个遍次中的第一遍,交叉编译到那台机器上,然后发现第二遍永远无法运行。
正当我思考如何处理这些问题,犹豫着是该尝试修复它们还是干脆写一个全新的编译器时,我以迂回的方式开始了 GNU Emacs 的工作。GNU Emacs 是 GNU 系统目前主要的发布部分。它是一个可扩展的文本编辑器,很像十年前我开发的那个原始的 Emacs,不同的是,这个版本使用真正的 LISP 作为其扩展语言。编辑器本身用 C 语言实现,LISP 解释器也是如此,所以 LISP 解释器是完全可移植的,你不需要在编辑器之外另有一个 LISP 系统。编辑器包含它自己的 LISP 系统,所有的编辑命令都是用 LISP 编写的,这样它们可以为你提供示例,供你学习如何编写自己的编辑命令,以及作为你修改的起点,从而将它们变成你真正想要的编辑命令。
那年夏天,大约两年前吧,我的一个朋友告诉我,由于他参与了 Gosling Emacs 的早期开发工作,他收到过 Gosling 发来的消息,允许他分发自己的那个版本。Gosling 最初开发了他的 Emacs,并自由分发,吸引了许多人帮助改进,当时人们是依据 Gosling 自己在手册中的话,期望他会遵循我开创原始 Emacs 时所秉持的那种精神。但后来他背叛了所有人,给程序加上版权,要求人们承诺不得再分发,然后把它卖给了一家软件公司。后来我和他个人的交往也表明,根据这段历史,你所能想象到的懦弱和可鄙,他一样都不差。
但无论如何,我朋友把这个程序给了我。我的打算是修改它的顶层编辑命令,使其与我习惯的原始 Emacs 兼容,让它们能够处理人们期望它们能处理的各种数值参数组合,并具备我想要的所有特性。但进行了一小部分工作后,我发现这个编辑器的扩展语言——叫做 MOCKLISP——不足以完成任务。我发现,为了按计划进行,我必须立即替换它。之前我曾想过或许有一天可以用真正的 LISP 替换 MOCKLISP,但我发现这件事必须得先做。MOCKLISP 之所以叫 MOCK,是因为它没有任何结构化的数据类型:它没有 LISP 列表,也没有任何形式的数组。它也没有 LISP 符号——即带有名称的对象:对于任何一个特定的名称,只有一个对象存在,这样你输入名称时,总是得到同一个对象。这极大地阻碍了许多类型程序的编写,你不得不通过复杂的字符串操作来完成本不该那样做的事情。
于是我编写了一个 LISP 解释器,用它替换了 MOCKLISP。在这个过程中,我发现必须重写编辑器的许多内部数据结构,因为我希望它们成为 LISP 对象。我希望 LISP 和编辑器之间的接口是干净的,这意味着像编辑器缓冲区、子进程、窗口和缓冲区位置这样的对象,都必须是 LISP 对象,这样操作它们的编辑器原语才能作为处理 LISP 数据的 LISP 函数被调用。这就意味着我必须重新设计所有这些对象的数据格式,并重写所有操作它们的函数。结果就是,大约六个月后,我几乎重写了编辑器里的所有东西。
此外,由于用 MOCKLISP 编写东西非常困难,所有用 MOCKLISP 写成的部分都非常不优雅。通过重写它们以利用真正 LISP 的强大功能,我可以让它们变得更强大、更简洁,也快得多。所以我这么做了,结果就是,当我开始分发这个程序时,最初收到的代码中只有很小一部分保留了下来。
此时,Gosling 自以为已出售程序的那家公司,对我朋友分发该程序的权利提出了质疑。那份授权消息存在备份磁带上,所以他找不到。Gosling 也否认曾给予他许可。然后一件奇怪的事发生了。他正与那家公司谈判,看起来该公司主要担心的是,不能有任何与他们正在分发的软件相似的东西被散布出去。你看,他仍在分发,而他供职的 Megatest 公司也仍在分发他给我的同一个东西——那实际上是 Gosling Emacs 的旧版本,加上他自己的修改。所以他打算与对方达成协议:他将停止分发那个版本,转而使用 GNU Emacs,而对方则将承认他确实拥有那份许可,这样按理说大家就皆大欢喜了。这家公司当时还跟我联系,表示希望分发 GNU Emacs(当然是免费的),同时也出售各种配套支持服务,他们还想聘请我来协助这项工作。所以,奇怪的是,他们后来改变了主意,拒绝签署那份协议,并在网络上发布消息,声称我不被允许分发这个程序。他们实际上并没有说他们会采取任何行动,只是说“不清楚他们将来某天是否会采取行动”。但这足以吓唬住人们,以至于再也没有人敢使用它了,这真是一件可悲的事情。
(有时我会想,也许我这辈子能做的最好的事情之一就是:找到一大堆作为商业秘密的专有软件,然后在街角开始分发其副本,这样它就不再是商业秘密了。也许这比我自己亲自编写软件,能更有效地为人们带来新的自由软件;但每个人都太懦弱了,甚至不敢去拿。)
所以我被迫重写了所有剩余的部分,我做到了,这花了我大约一个半星期。于是,他们赢得了一场巨大的胜利。而此后,我无论如何也绝不会再以任何形式与他们合作了。
待 GNU Emacs 足够稳定之后——这总共花了大约一年半的时间——我开始回归系统的其他部分。我开发了一个调试器,取名为 GDB,它是一个用于 C 代码的符号调试器,最近刚刚开始对外发布。这个调试器在很大程度上秉承了 DBX 的精神,DBX 是 Berkeley Unix 附带的一个调试器。命令由一个表示你想要做什么的单词加上参数构成。在这个调试器中,所有命令都可以缩写,常用命令有单字符的缩写,但任何唯一的前缀缩写也总是被允许的。它还具备可扩展的 HELP 功能,你可以输入 HELP 后跟任何命令甚至子命令,就能得到关于如何使用该命令的详细说明。当然,你可以输入任何 C 表达式,它会打印出表达式的值。
你还可以做一些在符号化 C 调试器中不常见的操作,例如:你可以引用任意内存地址上的任意 C
数据类型,无论是检查其值还是赋值。所以,比如你想在一个特定地址的字中存储一个浮点数值,你只需说:“给我这个地址上类型为 FLOAT 或 DOUBLE
的对象”,然后赋值即可。另一项功能是,你可以检查所有过去检查过的值。每个被检查过的值都会被放入“值历史”中。你可以通过其数值位置引用历史中的任何元素,或者直接用美元符号(`$`)轻松引用最后一个元素。这使得追踪链表结构变得容易得多。如果你有某种
C 结构体,其中包含一个指向另一个结构体的指针,你可以做类似 PRINT *$.next
的操作,意思是:“从我上次显示的东西中取出 next
字段,然后显示它指向的结构体。”你可以重复这个命令,每次都会看到链表中的下一个结构体。而在我所见过的所有其他 C
调试器中,唯一的办法是每次都输入一个更长的命令。当这个功能与直接按回车键重复上一条命令的特性结合起来时,就变得非常方便。对于你想查看的链表中的每个元素,只需按下回车键即可。
调试器中还有显式可设置的变量,可以有任意多个。你输入一个美元符号后跟一个名字,那便是一个变量。你可以为这些变量赋予任何 C
数据类型的值,之后就能对它们进行检查。这些变量的一些用途包括:如果你要频繁检查某个特定的值,与其记住它在值历史中的编号,不如给它起个名字。在设置条件断点时,你也会发现它们的用处。条件断点是许多符号调试器中的一项功能,你可以说“当程序执行到某处时停止,但仅在某个特定表达式为真的情况下才停止”。调试器中的变量允许你将程序中的某个变量与该变量先前保存在调试器变量中的值进行比较。它们的另一个用途是用于计数,因为毕竟赋值在
C 中是表达式,所以你可以执行 $foo+=5 来将 $foo 的值增加 5,或者直接执行
$foo++。你甚至可以在条件断点中这样做,所以这是一种实现“命中断点第十次才中断”的简便方法,你可以执行
$foo--==0。大家都理解这个吗?将 $foo 减一,如果现在它为零,则中断。然后你将
$foo 设置为你希望跳过的次数,然后让程序继续运行。你也可以用它来检查数组的元素。假设你有一个指针数组,你可以这样做:
PRINT X[$foo++]
不过你首先要
SET $foo=0
好的,当你执行那个操作时(指向 PRINT 表达式),你会得到 `X`
的第零个元素。再次执行,你会得到第一个元素。假设这些是指向结构体的指针,那么你可能需要在(PRINT 表达式中的 `X`
前面)加上一个星号,这样每次就会打印出数组元素所指向的下一个结构体。当然,你可以通过键入回车来重复这个命令。如果仅仅重复单个命令不够用,你还可以创建用户自定义命令。你可以输入
Define Mumble,然后输入几行命令,最后输入 end。这样就定义了一个名为
Mumble
的命令,它会执行那些命令行。将这些定义放在命令文件中会非常有用。你可以在每个目录下放一个命令文件,当你以该目录作为工作目录启动调试器时,这个文件就会被自动加载。这样,对于每个程序,你都可以定义一套用户自定义命令,以便用一种方便的方式来访问该程序的数据结构。你甚至可以为你的用户自定义命令提供文档,这样它们就能像内建命令一样被
help 功能所处理。
这个调试器另一个不寻常的地方是,它能够从堆栈中丢弃栈帧。因为我相信,重要的不仅仅是能够检查正在调试的程序中发生了什么,还要能够以任何可以想到的方式去改变它。这样,在你发现一个问题并知道错在哪里之后,你可以修正现场,就好像那段代码是正确的,然后继续寻找下一个缺陷,而无需先重新编译程序。这意味着不仅要能灵活地改变程序中的数据区,还要能改变控制流。在这个调试器中,你可以非常直接地改变控制流,只需输入:
SET $PC=<some number>
所以你可以设置程序计数器。你也可以设置堆栈指针,或者你可以输入:
SET $SP+=<某个值>
如果你想将堆栈指针增加一定的量,但除此之外,你还可以告诉它从程序的某一行开始执行,你可以将程序计数器设置为某个特定的源代码行。但如果你发现你不小心调用了一个函数,而实际上根本不想调用它,那该怎么办呢?比如说,那个函数已经乱成一团,你真正想做的是跳出它,然后手动完成本应由那个函数完成的操作。为此,你可以使用
RETURN 命令。你选择一个栈帧,然后输入
RETURN,它就会导致那个栈帧以及它内部的所有栈帧被丢弃,就好像那个函数此刻正在返回一样,你还可以指定它应该返回的值。这并不会继续执行;它假装返回发生了,然后再次停止程序,这样你就可以继续修改其他东西了。
将这些功能整合在一起,你就能对程序的运行状况有相当好的掌控力。
还有一个有点意思的地方:C语言有字符串常量。如果你在调试器里计算的表达式中使用了一个字符串常量,会发生什么?它必须在被你调试的程序中创建一个字符串。嗯,它确实做到了。它会在那个被调试的程序中设置一个对
MALLOC 的调用,让 MALLOC
运行起来,然后重新获得控制权。就这样,它悄无声息地找到了存放这个字符串常量的地方。
最终,当这个调试器在真正的 GNU 系统上运行时,我打算在其中加入一些功能,用来检查其下层运行进程的所有内部状态。例如,检查内存映射的状态,哪些页面存在,哪些可读,哪些可写,以及检查被调试程序的终端状态。现在已经有一点相关的命令了;这个调试器与 Unix 上的调试器不同,它将调试器和被调试程序的终端状态完全分开处理,因此它可以与运行在原始模式下的程序、使用中断驱动输入的程序一起工作。此外,还有一个命令可以让你了解被调试程序实际使用的终端设置的一些情况。我相信,总的来说,调试器应该让你能够查明被调试进程中正在发生的一切。
GNU 系统中已经存在的另外两个主要部分是:一个是新的 C 编译器,另一个是 TRIX 内核。
这个新的 C 编译器是我今年(从去年春天开始)编写的。我终于决定必须放弃 PASTEL 了。这个 C 编译器采用了一些来自 PASTEL 的想法,以及一些来自亚利桑那大学便携式优化器的想法。后者一个有趣的思路是:先生成简单的指令,然后在目标机器允许的情况下,将几条简单指令组合成一条复杂指令,以此来处理多种不同类型的机器。为了统一实现这一点,他们用代数符号来表示指令。例如,一条 ADD 指令可能会被表示成这样:
r[3]=r[2]+4
这将是他们编译器内部的一种表示形式,代表一条指令:将寄存器二的内容加上四,然后存入寄存器三。通过这种方式,你可以表示任何机器的任何可能的指令。所以他们确实用这种方法表示了所有指令,然后在需要尝试组合指令时,他们会通过将一个表达式替换到另一个表达式中,从而为组合后的指令生成一个更复杂的代数表达式。
有时,取决于第一条指令的结果是否还有其他用途,可能需要生成一条包含两个赋值操作符的组合指令。一个用于这个值(指向 ???),另一个用于将第二个指令的结果代入后的这个值(指向 ???)。但如果这个值只被使用了一次,那么在代入之后就可以消除它,无需再计算它。所以,正确地执行代入,并检查中间的指令是否改变了这些值以及其他类似情况,实际上相当复杂。当你开始支持诸如自动增量和自动减量寻址(我现在就在做)时,还必须对这些情况进行各种检查,以判断你正在进行的操作是否不保持值不变。
但在检查完所有这些之后,你会将替换后的组合表达式送入一个模式匹配器,该匹配器会识别你所选目标机器的所有有效指令。如果匹配成功,你就用组合后的指令替换那两条指令;否则,就保持不变。他们的技术正是通过这种方式,将数据流相关的两条或三条指令组合起来。
在亚利桑那大学的编译器中,他们实际上是用像这样的文本字符串来表示事物,因此他们的编译器慢得可怕。起初我曾想过直接使用他们的编译器并加以修改,但很快我就意识到,必须完全重写才能达到我想要的速度。所以,我重写了它,为所有这些表达式使用列表结构的表示。像这样:
(set (reg 2)
(+ (reg 2)
(int 4)))
这看起来像 Lisp,但这些符号的语义并不完全是
LISP,因为这里的每个符号都是被特别识别的。有一组特定的、预定义的符号集合,包含了所有你需要用到的。每个符号都有特定的参数类型模式,例如:reg
后面总是跟着一个整数,因为寄存器是编号的;而 +
则接受两个子表达式,依此类推。与每个表达式相关联的还有一个数据类型,它本质上说明了这个值是定点数还是浮点数,以及它占多少字节。如果需要的话,也可以扩展以处理其他类型。
我进行自动寄存器分配的方法是:当我最初生成这些代码,以及进行指令组合和所有相关操作时,对于每一个可能放入寄存器的变量,我会分配一个我称之为“伪寄存器编号”的东西。这个编号从16开始,或者从任何对于你的目标机器来说过高的数字开始,这样它就不会与实际寄存器冲突。因此,真实寄存器的编号是从0到15(或类似的范围),高于此范围的则是伪寄存器。然后,编译器的最后阶段之一就是遍历代码,将所有伪寄存器替换为真实寄存器。同样,这需要构建一个冲突图,查看哪些伪寄存器在同一时刻是活跃的,它们显然不能分配到同一个真实寄存器中。然后,编译器会尽可能地尝试将伪寄存器装填到真实寄存器中,并按照它们重要性的优先级进行排序。
最后,编译器还必须修正代码中出现的各种问题,例如,当存在无法放入真实寄存器的伪寄存器时,这些伪寄存器就必须被分配到堆栈槽位中。在某些机器上,当发生这种情况时,部分指令可能会变得无效。例如,在68000处理器上,你可以将寄存器加到内存,也可以将内存加到寄存器,但不能将一个内存位置加到另一个内存位置。所以,如果你有一条 ADD 指令,而目标平台是68000,并且两个操作数最终都在内存中,那么这条指令就是无效的。因此,这最后的遍次会遍历代码,并根据需要将数据复制到寄存器或从寄存器中复制出来,以修正这些问题。
索引寄存器也可能引发问题。如果你试图用某个量进行索引,那么在大多数情况下,如果这个索引量在内存中,代码就会变得无效——除非在某些机器的少数情况下,可以通过间接寻址来实现。在你对索引寄存器进行自动增量操作的情况下,你可能需要先将值复制到一个寄存器中,执行指令,然后再将递增后的值复制回它实际所在的堆栈内存槽位中。
这里面有很多复杂繁琐的问题需要处理,我还没有完成实现所有那些能使编译器真正达到最高效率所需的复杂处理。
这个编译器目前的运作方式是:首先,有一个解析器将 C 代码转换成带有 C 数据类型信息的语法树。然后,另一个遍次会检查这棵树,并生成像这样的代码(类似
LISP 的代码)。接着是几个优化遍次:一个遍次处理诸如跨跳转的跳转、跳转到跳转、跳转到 .+1
等情况,所有这些都可以立即简化。然后是一个公共子表达式识别器,接着是查找基本块并执行数据流分析,以便能判断每条指令中,哪些值被使用后不再被用到。同时,还要将每条指令与其所使用值的产生位置链接起来。所以,如果有一条指令生成了伪寄存器
R[28],而稍后的另一条指令是第一个使用 R[28]
的地方,我就会让第二条指令指回第一条指令。这个指针用于控制尝试组合指令的过程。你并不是组合相邻的指令,而是组合一条使用某个值的指令和产生那个值的指令。即使中间有其他指令,它们对此并不重要,你只需要检查它们,确保它们不会造成任何干扰。组合器之后是动态寄存器分配器,最后是将代码转换成汇编代码的部分。
在亚利桑那大学的编译器中,指令识别器是用 LEX 生成的。你的机器描述就是一个 LEX 程序,LEX 会把它转换成一个 C 函数,用于将有效指令识别为字符串。而我采用的方法则不同,是一个专用的决策树,它是根据用这种类似 LISP 的语法编写的机器描述生成的。这个识别器被用作编译器中许多不同部分的子程序。
目前,这个编译器的运行速度与 PCC 相当。如果你告诉它不要进行那种复杂的寄存器分配,它会快得多,在这种情况下,它分配寄存器的方式与 PCC 相同。在其超级复杂模式下,它在寄存器分配方面做得比 PCC 好得多,而且我观察到,对于 VAX 而言,它生成的代码是我见过的所有 VAX 上的 C 编译器里最好的。
对于68000处理器来说,生成的代码仍不理想。我能看到在早期阶段,有些处理并非最优,因为它无法完全预判后续情况。它在早期阶段会做一个选择,做它认为最好的选择,但实际上,如果它选择了另一个,后期阶段其实足够聪明,能做出更好的结果。但早期阶段不知道后期阶段会做什么,所以我需要在其中一些方面做更多的工作。
有时这会导致它不必要地释放寄存器。因为当某些值最终在内存中,而需要将它们复制到寄存器时,它就需要获得寄存器来存放这些值。这意味着要从已经分配好的寄存器中,把那些临时值踢到堆栈槽里去。当然,既然这些值现在在内存里而不是寄存器里,这可能会使更多指令变得无效,所以它不得不一遍又一遍地检查。有时它认为必须把某些值复制到寄存器,但实际上并不需要,于是它可能释放了太多东西,从而没有用到所有可用的寄存器。
[提问:你有针对32000的代码生成器吗?]还没有。但我要重申一下,它不是一个代码生成器,你需要的只是一个机器描述。也就是用这种(类似 LISP)形式描述的所有机器指令的列表。所以事实上,除了实现关于哪些参数可以在寄存器中以及哪种寄存器的约束条件——这是68000需要的,而 VAX 不需要——这项工作之外,将这个编译器从 VAX 移植到68000只花了几天时间。所以移植起来非常容易。
这个编译器目前可以生成汇编代码,并且能够生成调试信息——可以是 DBX 所需的格式,也可以是 GDB 特有的内部格式。我想说,这个编译器目前只需要在三个方面进行完善:第一,我需要增加一个“性能分析”功能,就像 Unix 编译器所带的那样。第二,我需要让这些寄存器分配的处理更智能,这样输出中就不会再出现那些愚蠢的代码了。第三,还有一些各种各样的缺陷,一些目前处理得不对的地方,尽管它已经能正确编译自身了。我估计这只需要几个月的时间,然后我就会发布这个编译器。
系统中另一个已存在的较大组成部分是内核。[提问:要休息一下吗?]啊,是啊,我想我们忘了中间休息这回事了。不如我先讲完内核,这大概只需要五分钟,然后我们再休息。
对于内核,我计划使用一个叫做 TRIX 的系统(据我所知,这个名字不代表任何含义),它是麻省理工学院的一个研究项目开发的。这个系统基于远程过程调用。因此,程序被称为域。每个域都有一个地址空间和各种能力,而所谓能力,无非就是调用另一个域的能力。任何域都可以创建“能力端口”供其他域调用它,然后它可以将这些端口传递给其他域,并且调用系统与调用另一个用户域之间没有区别。事实上,你无法分辨你调用的是什么。因此,由其他用户程序来实现设备变得非常容易。文件系统可以由一个用户程序透明地实现。跨网络通信也是透明的。你以为你直接调用了另一个域,但实际上你调用的是网络服务器域。它接收你在调用中提供的信息,通过网络传递给另一个服务器程序,然后那个程序再调用你试图与之通信的域。但你和那个目标域都察觉不到这个过程。
TRIX 内核已经可以运行,并且具有一定程度的 Unix 兼容性,但还远远不够。目前它有一个文件系统,在磁盘上使用的是与早期 Unix 文件系统相同的结构。这使得调试起来更容易,因为他们可以用 Unix 来设置文件,然后运行 TRIX。但是,这个文件系统不具备任何我认为必要的功能。
我认为必须添加的功能包括:版本编号、反删除功能、关于文件何时、如何以及在何处备份到磁带的信息,以及文件的原子替换。我认为 Unix
中有一个很好的特性是,当一个文件正在被写入时,你已经可以查看其中的内容,例如,你可以使用 tail
命令来查看写入进度,这非常棒。如果程序在写入一部分文件后崩溃,你也可以看到它产生了什么。这些都是好的。但是,这种部分写入的输出绝不应该被误认为是最终预期的完整输出。在此之前,该文件的旧版本应该继续可见,并被所有试图使用它的人使用,直到新版本被完整且正确地创建。这意味着新版本应该在文件系统中可见,但不应使用它最终应有的名称。它应该在完成后被重命名。顺便说一句,这在
ITS 中就是这样做的,尽管在 ITS 中每个用户程序必须显式地执行此操作。为了与现有的 Unix 用户程序兼容,这个过程必须对程序透明地发生。
我有一个巧妙而复杂的方案,试图让版本编号与现有的 Unix
用户程序兼容。思路是这样的:如果你以普通方式指定文件名,那么版本号是隐含的。但如果你希望精确指定一个名称,要么是因为你想明确说明使用哪个版本,要么是因为你根本不需要版本功能,你就在文件名末尾加上一个点。因此,如果你给的文件名是
FOO,它的意思是:“在 FOO 的现有版本中搜索,并取最新的一个。”但如果你说
FOO.,它的意思是:“精确地使用名称 FOO,不要别的。”如果你说
FOO.3.,它的意思是:“精确地使用名称 FOO.3”,这当然就是
FOO 的第三版,不要别的。在输出时,如果你只说 FOO,它最终会创建
FOO 的一个新版本;但如果你说 FOO.,它就会写入一个名为 FOO
的文件。
当然,要把所有这些细节都厘清,并找出是否存在任何遗留问题,比如某些 Unix 软件在接收到带有点的文件名后是否会出错,以及如何通过这种方式让它们获得预期的行为,这确实存在一些挑战。
我期望的是,当你打开一个以点结尾的文件名进行输出时,实际上应该立即打开那个确切的名称,这样你就能得到与 Unix 相同的行为——部分写入的输出立即可见。而当你输出一个不以点结尾的名称时,新版本应该在你关闭文件时(并且只有在你显式关闭它时)才出现。如果因为任务终止、系统崩溃或类似原因而关闭,它应该被保存在一个不同的名称下。
这个想法可以与“通配符匹配”联系起来。具体来说,如果一个文件名不以点结尾,那么在匹配时,它会与所有不带版本号的名称进行匹配。因此,如果一个目录中有像这样的文件:
foo.1 foo.2 bar.8
如果我输入 *,那么输出会是
foo bar
因为它会取所有名称,去掉它们的版本号,然后取所有不同的名称。但如果我说
*.,那么它会取所有确切的名称,在每个名称后面加一个点,然后进行匹配。这样就能得到所有存在的各个独立版本的文件名。类似地,你可以看到
*.c 和 *.c. 的区别:前者基本上会给你所有 .c
文件的、不带版本号的引用,而后者则会给你所有版本……嗯,实际上后者不行,你得说 *.c.*.
才行;我还没把这里的细节完全想清楚。
另一个虽然不是用户可见功能,但肯定可以加入且兼容的特性,是文件系统的故障安全机制。也就是说,通过以正确的顺序将信息写入磁盘,使得你可以在任何时候按下“halt”键,而不会因此损坏磁盘上的文件系统。如何实现这一点已经众所周知,我想不通为什么还有人会忽略它。另一个想法是增加冗余信息。我还不确定是否会这么做,但我有一些想法,比如在每个文件中存储它的所有文件名,这样一来,如果磁盘上的某个目录丢失了,就有可能根据磁盘上的其余内容将其重建。
而且,我想我知道如何实现文件任意部分的原子更新。也就是说,如果你想要用新数据替换文件中的某个子范围,其实现方式能够保证任何试图读取该文件的操作,要么只看到旧数据,要么只看到新数据。我相信我能做到这一点,甚至不需要任何锁机制。
对于网络支持,我最终打算在这个系统上实现 TCP/IP。同时,我认为也可以使用 KERMIT 协议来实现与 UUCP 实质上等效的功能。
Shell 我相信已经有人写好了。它有两种模式,在同一个程序中,一种模仿 Bourne shell,一种模仿 C shell。我还没有收到它的副本,也不知道需要对其做多少工作。此外,许多其他工具软件也已经存在。有 MAKE,有 LS,还有一个名为 BISON 的 YACC 替代品正在分发。有一个与 LEX 非常接近的东西也存在,但还不完全兼容,需要一些工作。总的来说,剩下要做的工作已经比已经完成的要少了,但我们仍然需要很多人来帮忙。
人们总是问我:“什么时候能完成?”我当然不可能知道什么时候能完成,但这不是你们该问我的问题。如果你们打算为此付费,那么想确切知道能得到什么以及何时得到,这很合理。但既然你们不打算付费,你们该问的正确问题是:“我怎样才能帮助它更快地完成?”我有一个项目列表,放在 MIT 的一个文件里。有兴趣帮忙的人可以按这个互联网地址给我发邮件,我会回复一份项目列表。(我想知道这样行不行(看着粉笔)。这个能看清吗?这是 “RMS@GNU.ORG”(跟着小球走)。现在我们休息一下,休息之后,我会说一些真正有争议的话题。所以别走开。如果现在走了,你们会错过真正的精彩部分。
[15分钟休息。休息时间]
有人让我告诉大家如何获取 GNU 软件的副本。当然,一种方式是,如果你有朋友有副本,你可以复制。但如果你不认识有副本的朋友,或者你没上互联网,无法用 FTP 获取,那么你总是可以订购一份发布磁带,并向自由软件基金会寄一些钱。当然,自由软件与免费分发不是一回事。这个我稍后会详细解释。
我这里有一本 EMACS 手册,印刷质量很好。它是先经照相排版,然后胶印而成的。虽然你也可以从 EMACS 发行版附带的源文件中自己打印,但你可以从自由软件基金会获得这些印刷好的副本。之后你可以过来看看这本手册,这里面也包含了一份订购单,你可以从中复制一些信息。还有这张封面图,有时也颇受喜爱。这个(指着图中被骑着 GNU 的 RMS 追赶的人)是一个被吓坏的软件囤积者,我一会儿就会讲到它。
软件是一个相对较新的现象。人们开始分发软件大概是三十年前的事。直到大约二十年前,才有人想到围绕它来开展业务。这是一个全新的领域,对于人们应该如何行事,或者任何人拥有什么权利,都没有既定的传统。人们尝试通过类比,将生活中其他领域的传统引入进来,有好几种不同的想法。
一种在欧洲许多教授中比较流行的类比,是将程序与数学相类比。程序就像是一个大型公式。而传统上,没有人可以拥有一个数学公式。任何人都可以复制和使用它们。
对普通人来说最有意义的类比是菜谱。仔细想想,日常生活中与程序最相似的东西就是菜谱了——它是一套操作指南。区别在于,菜谱是由人来执行,而不是由机器自动执行。确实,菜谱没有源代码和目标代码之分,但它仍然是最接近的类比。而且,没有人可以拥有一个菜谱。
但最终被选中的类比是书籍,因为它们有版权。为什么会做出这个选择呢?因为那些能从这个特定选择中获益最多的人,被允许来做出这个决定。是编写程序的人,而不是使用程序的人,被赋予了决定权。他们以一种完全自私的方式做出了决定,结果就是把编程领域变成了一个丑陋的地方。
当我1971年开始在 MIT 工作时,我们开发出的程序竟然可以不共享——这种想法根本就没被讨论过。斯坦福、卡内基梅隆,还有所有地方,甚至 DEC 公司,都是如此。那时 DEC 公司的操作系统是免费的。我经常从 DEC 系统那里得到一些程序片段,比如一个 PDP-11 的交叉汇编器,然后我把它移植到 ITS 上运行,并添加了许多功能。那个程序没有任何版权。
直到七十年代末,这种情况才开始改变。我们当时所拥有的那种分享精神,给我留下了极其深刻的印象。我们做的是我们认为有用的事情,如果有人能用上,我们就感到很高兴。所以,当我开发出第一个 EMACS,并且人们开始想在 MIT 之外使用它时,我说它属于 EMACS“公社”。要使用 EMACS,你就必须成为公社的一员,这意味着你有责任贡献你所做的所有改进。所有对原始 EMACS 的改进都必须发回给我,这样我就能把它们整合到新版本的 EMACS 中,让社区里的每个人都能从中受益。
但这种情况开始被破坏,始于 CMU 开发出 SCRIBE,然后把它卖给了一家公司。这让我们许多大学里的人感到非常不安,因为我们看到,这是一个摆在每个人面前的诱惑:不合作是如此有利可图,而我们这些仍然信奉合作的人,却没有任何武器去强制人们与我们合作。显然,人们会一个接一个地背叛,停止与社会的其他部分合作,直到最后只剩下我们这些良心非常强的人还在坚持合作。而事情的发展,正是如此。
如今的编程领域已经变得丑陋不堪,每个人都愤世嫉俗地盘算着,通过对同行和用户不友好,自己能捞到多少钱。
我想要论证,软件所有权的做法既是物质上的浪费,对社会精神有害,而且是邪恶的。这三者是相互关联的。它之所以精神有害,是因为它让每个接触计算机的社会成员都卷入一种做法中,这种做法显然对他人造成了物质浪费。而每当你为了自己的利益做一件事,并且明知它对别人的伤害超过了对你的帮助时,你就不得不在心里变得愤世嫉俗,才能支持这样的事。它之所以邪恶,是因为它蓄意浪费社会所付出的劳动,并导致社会衰败。
首先,我想解释一下,试图拥有软件和其他普遍有用的信息会造成哪些危害。然后,我将驳斥那些为支持这种做法而提出的论点。最后,我想谈谈如何对抗这种现象,以及我自己是如何与之斗争的。
拥有信息的想法在三个不同层面是有害的。在三个不同层面造成物质上的危害,而每一种物质上的危害,都有其对应的精神上的危害。
第一个层面是,它仅仅阻碍了程序的使用,导致使用程序的人变少。但事实上,为一个程序,让更少的人使用,并不会减少制作它所投入的工作量。当你对程序的使用标上价格时,这就成了一种激励——这是那些软件囤积者喜欢用的词——这个价格就是在激励人们不要去使用这个程序,而这是一种浪费。例如,如果因为程序有价格,导致使用它的人数只有一半,那么这个程序就有一半被浪费了。同样的工作量只产生了一半的财富。
事实上,你根本不需要做任何特别的事情,程序就能传播到所有想用它的人那里,因为人们自己就可以完美地复制它,它自然会到达每个人手中。你写完程序之后,只需要袖手旁观,让人们去做他们想做的事就够了。但现实并非如此;相反,有人故意试图阻碍程序的分享。而且,他不仅仅是试图阻碍,他还试图施压让别人也来帮忙。每当一个用户签署保密协议时,他基本上就背叛了他的同胞用户。他没有遵循那条黄金法则,说“我喜欢这个程序,我的邻居也会喜欢这个程序,我希望我们俩都能拥有它”,反而说:“行,给我吧。让我的邻居见鬼去吧!我会帮你保密,不让我的邻居拿到,只要给我就行!”正是这种精神造成了精神上的伤害。这种态度就是在说:“让我的邻居见鬼去吧,给我一份拷贝!”
自从我遇到一些人说,因为他们签了某个保密协议,所以不能给我某样东西的副本之后,当有人让我签类似的东西时,我就知道这是不对的。我不能对别人做出那种事——那种当初发生在我身上时让我无比愤怒的事。
但这只是危害的一个层面。第二个层面的危害出现在人们想要修改程序的时候,因为没有哪个程序能真正满足所有想要使用它的人的需求。就像人们喜欢调整菜谱,比如少放点盐,或者加些青椒一样,人们也需要修改程序来获得他们想要的效果。
现在,软件所有者其实并不真正关心人们是否能修改程序,但阻止人们修改却有利于他们的目的。通常,当软件是专有的时候,你无法获得源代码,也就无法修改它。这导致了程序员大量的工作浪费,也给用户带来许多挫折感。例如:我有一个朋友告诉我,她在一家银行当程序员,花了几个月时间编写一个新程序。其实当时有一个商业程序几乎能满足他们的需求,但就差那么一点点,对他们来说实际上根本没用。让它满足需求所需的修改量可能很小,但因为无法获得该程序的源代码,所以根本不可能。她不得不从头开始,浪费了大量的工作。我们只能推测,全世界有多少比例的程序员正在以这种方式浪费他们的时间。
还有一种情况是,程序勉强能用,但用着很不舒服。例如:MIT 第一次拥有图形打印机时,我们自己编写了软件,加入了许多好用的功能,比如打印任务完成时会给你发消息,打印机缺纸而你的任务在队列中时也会通知你,还有很多我们想要的其他功能。后来我们得到了一台好得多的图形打印机,是最早的激光打印机之一,但它的软件是由施乐提供的,我们无法修改。他们不愿意加入这些功能,我们也不能,所以只能忍受着那些“半吊子”的功能。明知道自己准备好、愿意也有能力去修复它,却不被允许,这非常令人沮丧。我们被破坏了。
还有所有那些使用电脑的人,他们说电脑对他们来说是个谜,他们不知道电脑是如何工作的。他们怎么可能知道呢?他们读不到自己正在使用的程序的源代码。人们学习程序应该如何编写,或者程序是如何实现其功能的,唯一的方法就是阅读源代码。
所以我只能想,那种认为用户只把电脑当作工具的想法,是不是其实是一个自我实现的预言,是源代码保密做法所导致的结果。
与这种物质上的危害相伴的精神上的危害,体现在一种自足的精神层面。当一个人花费大量时间使用计算机系统时,这个计算机系统的配置就变成了他居住的城市。就像我们的房屋和家具的布局决定了我们生活在其中的感受一样,我们使用的计算机系统也是如此。如果我们无法改变所使用的计算机系统来适应我们,那么我们的生活实际上就受制于他人。看到这一点的人,在某种程度上会变得沮丧:“试图改变这些东西也没用,它们永远都会那么糟糕。甚至不值得去费心。我只要熬过这段时间……等结束了,我就离开,尽量不再去想它。”这种缺乏热情的精神,这种不热心,正是当你有公共精神却又不被允许把事情做得更好时所产生的结果。
第三个层面的危害在于软件开发者之间的互动。任何知识领域,只有当人们能够在前人工作的基础上继续发展时,才能取得最大的进步。而信息所有权的设立,恰恰就是为了阻止其他人这样做。如果人们可以在他人工作的基础上继续发展,那么所有权就会变得不清晰。所以他们确保每个进入该领域的新手都必须从头开始,从而极大地减缓了该领域的发展。
所以我们能看到:有多少个电子表格系统是由不同的公司各自独立开发的,却没有从之前是如何实现的当中获得任何益处?没错,第一个电子表格写得并不完美。它可能只运行在特定类型的电脑上,并且某些方面的处理方式并非最优。因此,不同的人有各种理由想要重写它的某些部分。但如果他们只需要重写那些他们真正想改进的部分,那么工作量就会少得多。你可能知道如何改进系统的某个方面,但你可能不知道如何改进同一个系统的另一个方面,事实上,你甚至可能很难做到和原来一样好。现在,如果你能采用你喜欢的部分,只重新开发你有了新想法的那个部分,你就能得到一个各方面都更好的系统,而工作量比现在编写一个全新的系统要少得多。而且我们都知道,一个系统常常能从完全重写中获益,但那也只有在你首先能阅读旧系统的前提下才有可能。
因此,编程领域的人们已经形成了一种浪费大量时间的方式,从而制造出对程序员的需求比实际需要更多的假象。为什么会有程序员短缺的现象?因为通过知识产权,程序员们安排浪费了他们所做的一半工作,所以我们似乎需要两倍数量的程序员。所以,当人们指着知识产权制度说“看看那庞大的就业数据,看看这个行业有多大”时,这实际上证明的恰恰是人们正在浪费大量的金钱和时间。如果他们谈论寻找提高程序员生产力的方法,如果这涉及到更优秀的工具,他们很乐意这么做。但要通过消除那些明确用来降低程序员生产力的做法来提高程序员生产力,他们却会反对。因为那会减少受雇程序员的数量。这有点精神分裂的味道。
与这一层面的物质危害相对应的精神危害,是对科学合作精神的损害。这种精神曾经如此强烈,以至于即使是交战国家的科学家也会继续合作,因为他们知道他们所做的工作与战争无关,仅仅是为了人类的长期利益。而如今,人们不再关心人类的长期利益了。
为了理解阻碍程序使用是什么样的一种情况,让我们想象一下,我们有一个三明治,你可以吃它,但它不会被消耗掉。你可以吃它,另一个人也可以吃它,同一个三明治,可以吃任意多次,而且它始终和原来一样有营养。
对于这个三明治,最好的做法,也是我们应该做的,就是带着它去有饥饿人群的地方,让它尽可能多地喂饱更多的人。无论如何,我们都不应该对这个三明治收取食用费,因为那样人们就吃不起,它就被浪费了。
程序就像这个三明治,甚至更有过之,因为它可以同时存在于许多不同的地方,被不同的人一个接一个地使用。就好像这个三明治足够永远养活世界上的每一个人,但这却是不被允许的,因为有人认为他应该拥有它。
现在,那些认为他们可以拥有程序的人,通常会提出两种论点来支持这一点。第一种是:“这是我写的,它是我精神的孩子,我的心血,我的灵魂都在里面。别人怎么能把它从我这里拿走?无论它去哪里,都是我的,我的,我的!!”嗯,有点奇怪的是,他们中的大多数人都会签署协议,声明程序属于他们为之工作的公司。
所以我相信,这是一种你很容易说服自己相信它很重要的事情,但你也同样可以轻易地说服自己它根本无关紧要。
通常,这些人会用这个论点来要求控制他人如何修改程序的权利。他们说:“没人能糟蹋我的艺术作品。”好吧,想象一下,如果你打算做一道菜,发明这道菜的人有权控制你如何烹饪,因为这是他的艺术作品。你想少放点盐,他却说:“哦,不行。我设计这道菜时,就必须放这么多盐!”“但我的医生说,我吃盐不安全。我该怎么办呢?”
显然,使用程序的人与这件事本身要贴近得多。程序的使用直接影响着他,而对于编写程序的人来说,这只是一种抽象的关系。因此,为了尽可能让人们掌握对自己生活的控制权,必须由用户来决定这些事情。
他们的第二种论点是经济方面的。“人们编程怎么获得报酬呢?”他们问道,这里面确实有那么一点点实质问题。但他们说的很多话都是混淆视听。混淆之处在于,一方面说“如果我们想让很多人编程,我们必须安排他们不需要以其他方式谋生”,另一方面又说“我们需要维持现行体制,你需要通过编程致富”,这两者根本不是一回事。仅仅维持生计的工资和程序员(至少在美国是如今的状况)赚的那种钱之间,有天壤之别。他们总是说:“我拿什么吃饭?”但问题其实不是“他能否吃上饭?”,而是“他如何能吃上寿司?”;他们说:“我如何能有片瓦遮头?”,但真正的问题是“他们怎么能买得起一套公寓?”
现行的体制是由那些投资于软件开发的人选择的,因为它给了他们赚取尽可能多金钱的可能性,而不是因为这是人们能够拿出资金来支持系统开发工作的唯一途径。事实上,就在十年甚至十五年以前,通过其他方式来支持软件开发还是很常见的。例如,那些 DEC 公司的免费操作系统,即使在七十年代初,也是由那些为工作而获得报酬的人开发的。许多有用的程序是在大学里开发的。如今这些程序常常被出售,但十五年前它们通常是免费的,然而开发它们的人也是为工作而获得报酬的。
当你拥有像程序这样的东西——就像一个无限的三明治,一条路——它只需要建造一次,一旦建成,你用多少次都无关紧要,使用它没有成本,通常我们不对使用它收费会更好。我们现在开发的许多东西都是这样,我们付钱让人去建造它们。例如,外面所有的街道。找到愿意免费编程的人是很容易的;但要找到愿意免费修路的人则根本不可能。修路不像编程那样具有创造性和趣味性。但我们外面有大量的街道,我们确实能想出办法来付钱给修路的人,而且我们这样做比另一种方式要好得多——比如我们说:“让公司去修路,并设立收费站,这样你每次拐过一条街道,就得付一次过路费。然后那些选了好地方修路的公司就会盈利,其他的就会破产。”
有趣的是,每当有人想出通过囤积某物来赚大钱的方法时,在那之前可能会有许许多多真正热情并渴望在该领域工作的人,他们唯一的问题就是如何才能勉强维持生计。以数学家为例,想要成为纯粹数学家的人数,远远超过能资助任何人成为纯粹数学家的经费。即使你得到了资助,也得不到很多,他们生活得并不好。音乐家的情况就更糟了。我看到过一个统计数据,关于马萨诸塞州的普通音乐家,也就是那些将大部分时间投入到成为音乐家上的人,他们的收入中位数大概是该州收入中位数的一半或更少。这勉强够糊口,很艰难。但仍然有很多人试图以此为业。然后,不知何故,当某个领域普遍有可能获得高额报酬时,所有这些人都消失了,人们开始说:“除非给那么高的报酬,否则没人会做这事。”
我亲眼目睹了这种情况在编程领域发生。就是那些曾经在人工智能实验室工作、收入微薄却乐在其中的同一些人,如今却无法想象年薪低于五万美元的工作。发生了什么?当你在人们面前晃动着赚大钱的可能性,当他们看到做类似工作的其他人能拿到那么多钱时,他们就会觉得自己也应该得到同样的报酬,于是没有人愿意继续过去的方式了。这种情况发生后,人们很容易认为支付高薪是唯一可行的方式,但事实并非如此。如果赚大钱的可能性不存在,就会有人愿意接受较低的报酬来做这件事,尤其是当这件事具有创造性和趣味性的时候。
我亲眼目睹了人工智能实验室那个独特的世界被摧毁,也看到出售软件正是摧毁它的一个内在部分。同时,正如我之前解释过的,我也看到了要拥有那样的社区,就必须有自由软件。但进一步思考后,我意识到了软件囤积危害整个社会的所有这些方式,尤其是通过施压让人们背叛邻居,从而导致社会衰败。那种精神,就是看着街上有人被捅却不去报警的精神。那种我们能在周围如此多的公司身上时时看到的精神。我很清楚,我有一个选择:我可以成为那个世界的一部分,对自己所做的事情感到不快乐;或者,我可以决定与之斗争。所以我决定斗争。我将我的职业生涯奉献给了重建软件分享社区,奉献给了试图终结囤积普遍有用信息这一现象。而 GNU 系统正是实现这一目标的手段。它是实现社会目标的技术手段。借助 GNU 系统,我希望为用户接种疫苗,以抵御软件囤积者的威胁。
现在,囤积者基本上声称有权让一个人的电脑变得无用。过去在美国,最常见的是大约五十年前,黑手党会去商店和酒吧,尤其是当时酒吧还是非法的时候。他们会走过去说:“最近这附近很多地方都着火了。你肯定不想让你的地方也着火吧?嗯,我们可以保护你免受火灾,你只需每月付我们一千美元,我们保证你这里不会着火。”这就是所谓的“保护费勒索”。现在我们也有类似的情况:有人说:“你这有台不错的电脑,上面还运行着一些程序。嗯,如果你不想这些程序消失,如果你不想警察来找你麻烦,你最好付我一千美元,我就给你一份带许可证的程序副本。”这就是所谓的“软件保护费勒索”。
实际上,他们所做的一切就是妨碍其他所有人去做需要做的事,但他们对自己,也对其他人,都假装是在提供有用的功能。嗯,我希望的是,当那个软件黑手党的人走过来,说:“你想让你的电脑上那些程序消失吗?”用户能够回答说:“我不再怕你了。我有这些自由的 GNU 软件,你现在拿我没办法了。”
现在,人们有时为拥有软件提出的一个理由是,认为这给了人们创作的动力。总的来说,我支持私营企业的理念,也支持通过生产人们喜欢用的东西来赚钱的想法,但这在软件领域已经走偏了。生产一个专有程序,与生产同样的程序但让它成为自由软件,对社会的贡献是不一样的。因为编写程序只是对社会的潜在贡献。对社会财富的真正贡献,只有在程序被使用时才会发生。如果你阻止程序被使用,那么贡献实际上就没有发生。所以,社会需要的不是这些每个人都那么有动力去生产的专有程序,我们真正需要的是自由软件。我们的社会之所以走偏,是因为它激励人们去做不太有用的事,却没有激励人们去做有用的事。因此,私营企业的基本理念并没有被遵循,你甚至可以说这个社会是病态的。毕竟,当一个人鼓励对自身不利的行为时,我们称之为病态。而社会现在正是以这种方式行事,鼓励程序员去做对社会不利的事。
我与常人不同。我更愿意相信自己是一个好的社会成员,正在为社会做贡献,而不是感觉自己成功地剥削了社会。这就是为什么我决定做我所做的事。但至少每个人都会因为觉得自己拿钱在做并非真正有用的事情而感到一丝不安。所以,让我们停止为这种激励人们做错事的想法辩护吧,让我们至少尝试提出一些安排,来鼓励人们做正确的事,也就是创造自由软件。
谢谢你们。
[此后,RMS 回答了大约一个小时的提问。在这个版本中,我只收录了极少数问答。录音带质量不佳,我也没有时间对所有内容进行妥善处理。]
- 问:有没有人试图找过你的麻烦?
答:唯一一次有人试图找我麻烦,是那个所谓的、自封的 Gosling Emacs 的“所有者”。除此之外,他们没有理由这么做,所以也做不了什么。顺便说一句,我想请大家注意一下,人们是如何使用语言来试图鼓励你思考某些想法,而忽略其他想法的。该领域当前流行的许多术语,都是由那些自封的软件所有者挑选的,目的是试图让你把软件看作与作为财产的物质对象相似,并忽视其中的差异。最明显的例子就是“盗版”这个词。请拒绝使用“盗版”来形容那些希望像好公民一样与邻居分享软件的人。
我忘了告诉你们这一点:版权的概念是在印刷机发明之后才出现的。在古代,作者们自由地互相抄写,这并不被认为是不对的,而且甚至非常有用:某些作者的作品之所以能留存下来,哪怕是片段,正是因为它们被其他留存下来的作品长篇引用。
这是因为当时书籍是一次一本被抄写的。制作十本副本的难度是制作一本的十倍。后来印刷机发明了,这并没有阻止人们用手抄书,但与印刷相比,手抄是如此令人不快,以至于它几乎等同于不可能。
当书籍只能通过批量生产来制作时,版权开始变得有意义,而且它也没有剥夺公众读者的自由。作为一个不拥有印刷机的公众成员,你反正也无法复制一本书。所以,仅仅因为有版权,你并没有失去任何自由。因此,版权的发明,以及它在道德上的合理性,都是源于技术的变革。现在,相反的变革正在发生。个人复制信息的能力正变得越来越强,我们可以看到,技术的最终进步将是使复制任何类型的信息都成为可能。[磁带翻面导致的停顿]
因此,我们又回到了古代世界那种版权毫无意义的情况。
如果我们思考一下我们关于财产的观念,它们源于物质对象。物质对象基本上遵循一个守恒定律。是的,我可以把一根粉笔掰成两半,但这不一样,而且物体会磨损、被消耗。但基本上,这是一把椅子(指向一把椅子)。我不能打个响指就变出两把椅子。得到另一把椅子的唯一方法,就是用和造第一把一模一样的方式再造一把。这需要更多的原材料,更多的生产劳动。我们关于财产的观念,就是为了在道德上适应这些事实而演化出来的。
但对于一条任何人都能复制的信息来说,事实就不同了。因此,与之相适应的道德态度也就不同。我们的道德态度来源于思考做某件事会在多大程度上帮助人们,又会在多大程度上伤害人们。对于物质对象,你可以过来拿走这把椅子,但你无法过来复制它。而且如果你拿走了椅子,它就不能再产生任何东西了,所以没有借口。如果有人说:“我花了力气做了这把椅子,而且只有一个人能拥有它,那这个人很可能就是我。”我们可能会说:“嗯,有道理。”当有人说:“我刻录了这张磁盘上的数据,只有一个人能拥有这张磁盘,所以你胆敢把它从我这里拿走。”嗯,那也说得通。如果只有一个人能拥有这张磁盘,那它归拥有这张磁盘的那个人所有也未尝不可。
但当另一个人走过来,说:“我不会弄坏你的磁盘,我只是要神奇地变出另一个一模一样的磁盘,然后把它拿走,之后你还可以像以前一样继续用这张磁盘。”嗯,这就好比有人说:“我有一个神奇的椅子复制机。你可以继续享受你的椅子,坐在上面,想用的时候它总是在那儿,但我也会有一把椅子。”这是好事。
如果人们不需要建造,只需要打个响指就能复制它们,那真是太棒了。但这种技术变革并不适合那些想要拥有单个副本并能通过单个副本赚钱的人。这种想法只适用于守恒的物体。所以他们竭尽全力让程序看起来像物质对象。你有没有想过,为什么你去软件商店买一个程序副本时,它被包装得像一本书一样?他们希望人们认为他们得到的是一个物质对象,而不是意识到他们真正得到的是可复制的数字数据形式。
归根结底,计算机不就是一台通用机器吗?你可能学过通用图灵机,那种可以模仿任何其他机器的机器。通用机器之所以如此出色,是因为你可以让它模仿任何其他机器,并且这些指令可以被复制和修改——而这恰恰是物质对象无法做到的。而这些,也正是软件囤积者想要阻止公众去做的事情。他们想从技术变革中获益,即从专用机器发展到通用机器,但他们不希望公众也获得这种益处。
本质上,他们是在试图维持“物质对象时代”,但那个时代已经过去了。我们应该让我们的对错观念,与我们生活的这个世界的实际情况保持一致。
- 问:所以这归根结底是信息所有权的问题。你认为,在你的观点里,有没有任何情况下,拥有信息是正当的?
答:对于那些不是普遍有用,或者属于个人性质的信息,我认为是可以的。换句话说,不是关于“如何做某事”的信息,而是关于“你打算做什么”的信息。这种信息对他人唯一的价值是投机性的,也就是说,他们可能从你这里拿走一些钱,但实际上并不能用它来创造任何东西。我认为,将这类事情保密并加以控制是完全合理的。
但对于创造性信息,对于那些人们可以使用或享受,并且拥有的人越多,就越能被使用和享受的信息,我们应该始终鼓励复制。
脚注
- 到 2025,Lisp Machine 操作系统 仍然维护运行 在一些现代计算机上。