《Usenet的早期历史》一文是 Usenet 的创始人之一 Steven M. Bellovin 教授在2019年,即 Usenet 诞生 40 周年所写下的一系列博客文章,The Nexus 在获得了 Bellovin 教授的授权后,将此系列博文翻译为中文,并将其分为上、下两篇发布。上篇包括序言、技术背景、硬件和经济因素、文件格式以及实现和用户体验。
第一部分:序言
根据我的回忆,2019 年 11 月是 Usenet 诞生 40 周年。(什么是 Usenet?维基百科上的词条虽然不错,但并不完美。)原本我打算写一篇正式论文,但现在决定改写一系列不定期更新的博客文章。今晚就先写第一部分吧。
第二部分:技术背景
差不多在 40 年前的这个时候,Usenet (也被称为 Netnews)诞生了。想要了解它的起源以及某些决策背后的原因,我们需要理解当时的技术限制。
作者注:这是一段根据我的个人记忆写下的历史;那个时候我们所有人都没有做记录,所以很有可能出现错误。我的大脑细胞连奇偶校验的功能都没有,更别提 ECC(Error Checking and Correcting,指令纠错技术)纠错了。所以大家如果在文中发现错误,请一定告诉我。
1979年,大型机仍然大行其道,主宰着计算领域。而 IBM 的个人计算机(PC)要两年之后才推出;那时的微型计算机能力有限,难以胜任重要任务。在某些领域,尤其在研究实验室和过程控制系统(process control systems)中,人们通常使用所谓的“迷你计算机”,这些计算机在当时看来机身较小(差不多有一两台冰箱那么大)。此外,当时还出现了一种名为“超级迷你计算机”的新型计算机,虽然它的 I/O 带宽不如大型计算机,但 CPU 的原始处理能力却可以与大型计算机相媲美。
当时,Unix 运行在一款流行的迷你计算机上,即由 DEC(Digital Equipment Corporation,数字设备公司)推出的 PDP-11。该计算机拥有 16 位地址空间(在适当的操作系统上,你可以使用一个 16 位地址空间存放指令,另外一个存放数据,这样就能近似地将地址空间翻倍);具体内存则取决于计算机型号,从数十千字节(是的,千字节)到几兆字节不等。单个程序一次最多只能访问 64K 的内存,但额外的物理内存意味着进行上下文切换时通常无需换页,因为其他进程可能仍然驻留在内存中(memory-resident)。
(请注意:我这里提到的是“换页”,而非“分页”;当时的 Unix 系统还无法实现分页。因为每个进程的内存都太少,分页并不划算,还不如直接把整个进程写入磁盘来得简单……)
对于当时的大多数人来说,网络是不存在的。虽然 ARPANET 已经问世(我当时已经用过),但只有国防部承包商或是拥有 DARPA(The Defense Advanced Research Projects Agency,美国国防部高级研究计划局)研究合同的大学才能接入。IBM 确实提供了一些基于租赁同步通信线路的网络服务,还有一些用于拨号批处理远程作业输入的老式机制,此外还有(至少一个)公共分组交换网络,不过连接到该网络的机构非常少。当时唯一稍微普及的设备是拨号调制解调器,但其速率只有300 bps。Bell 212A 拨号调制解调器刚刚推出,却非常罕见。你知道为什么吗?因为你几乎只能从电话公司租用它,而这家公司就是贝尔公司,正式名称为AT&T (美国电话电报公司)。
严格来讲,人们可以合法购买调制解调器,但要将它们直接连接到电话网络,就必须使用一种被称为 DAA(data access arrangement,数据访问安排)¹的租用适配器来“保护电话网络的正常运行”(解释这个概念涉及到复杂的电话监管问题,我就不在这里介绍了)。不过,Usenet 所处的监管环境略有不同:杜克大学由杜克电信(Duke Telecom)提供服务,这是一家隶属于大学的实体(达勒姆市属于通用电话电子公司服务范围)。而我当时就读的北卡罗来纳大学教堂山分校则由教堂山电话公司(Chapel Hill Telephone)提供服务。尽管那时州立法机构要求大学剥离公用设施,但教堂山分校仍旧拥有电话、电力、供水和污水处理系统。
想要了解 Usenet 的完整历史,还需要知道当时北卡罗来纳大学和杜克大学计算机科学系的计算环境。杜克大学使用的是高端机型 PDP-11/70,上面运行着Unix。我们这边有一台 PDP-11/45,原本是专门用来做分子图形建模的;它运行的是 DOS 系统( DEC 推出的一个小操作系统)。虽然它有几个多出来的终端端口,但这些端口甚至连调制解调器控制线都没有,也就是说,它们无法判断连接是否断开。我们把这些端口接到了北卡来罗纳大学计算中心的 Gandalf² 端口选择器上。在杜克大学的帮助下,我和一些同学在 PDP-11/45 上安装了第 6 版 Unix 系统,将它作为临时操作系统。一些教职人员对此非常感兴趣,他们筹集资金购买了一个更好的 8 端口终端适配器和一些存储器(虽然那时候半导体存储器的价格越来越低,但当时购买的很可能是磁芯存储器)。不久之后,我们获得了两台 VAX-11/780 计算机,但 Usenet 最初就是在这台小而慢的 PDP-11/45 上诞生的。而促成 Usenet 诞生最直接的动机是升级到第 7 版 Unix 的需求。在第 6 版 Unix 上,杜克大学使用了一个他们从别处获得的修改程序,为用户登录时提供公告功能,即向用户发送消息。但总是发送此类消息并不可取,因为在 300 bps 的速率下(即每秒 30 个字符),打印一条五行的消息要花费很长时间(没错,我指的是“打印”而不是“显示”;硬拷贝打印终端³在当时仍然非常普遍)。这个修改程序与第 7 版 Unix 的登录命令完全不兼容,所以必须重新实现。而且第 7 版 Unix 还拥有uucp
(Unix to Unix Copy,一种用于在 Unix 系统之间传输文件、邮件和执行命令的早期网络协议)。正是这些因素为 Usenet 的诞生奠定了基础。
第三部分:硬件和经济因素
在杜克大学计算机科学系,我们举行了一次计划会议,会议上所讨论的内容成为了后来的 Usenet。我们当时很清楚要做三件事:
⦁ 我们需要一个可以在本地用于发送管理消息的系统。
⦁ 我们需要一个联网的系统。
⦁ 我们将使用uucp
进行站点间通信。
最后一个决定可以说是默认选择,因为对于我们和大多数运行标准 Unix 的站点来说,没有其他可能。而且,运行uucp
只需要一个拨号调制解调器端口。[我不记得是谁最先提出了网络系统的想法,但我认为是杜克大学的研究生Tom Truscott和Jim Ellis(已故)⁴。]
但使用uucp
进行站点间通信还存在一个问题:谁来拨号?这个问题既涉及经济因素,又涉及技术和经济之间的权衡。后者源于当时的监管环境:固定调制解调器(hard-wired modem)在当时非常罕见,能够自动拨号的调制解调器更是闻所未闻(著名的 Hayes 智能调制解调器⁵还要再过几年才推出)。官方的解决方案是租用 Bell 801 自动拨号器,并使用 DEC DN11(由 DEC 生产的自动呼叫单元接口,用于将电话连接到计算机)外围设备作为计算机与 Bell 801 之间的接口。但这对于一个小型的研发项目来说,根本行不通。因为管理如调制解调器或 DN11 这样的一次性采购已经够困难了,更别提让老师们支付自动拨号器的月租费了。幸运的是,Tom Truscott和Jim Ellis已经解决了这个问题。
当时有一种价格低廉又可以买到的调制解调器:声学耦合器(acoustic coupler) ⁶ 。它的工作原理很直观:将电话听筒紧密地放在一对凹槽内,然后用电线将电子部分连接到计算机上。当计算机发送数据时,声学耦合器通过一个小扬声器发出声音,并将其发送到听筒的麦克风。同样,声学耦合器内的麦克风监听这些代表 0 或 1 的声音,并将相应的电压信号发送到计算机。由于与电话网络的唯一连接是通过声音,所以电话公司对此也毫无办法。(事实上,AT&T 几年前曾试图反对,但被驳回了。 ⁷)
这种方法在手动拨号时非常方便 —— 你只需拿起电话听筒、拨号,然后把听筒放入声学耦合器即可。但是,计算机要如何利用这个设备来进行拨号呢?这就是它的巧妙之处。计算机通过一个叫做 RS-232 ⁸ 的标准与声学耦合器连接在一起。其中有五个引脚尤为重要:地线、发送 、接收、CD(载波检测,carrier detect)和DTR(数据终端就绪,data terminal ready)。(顺便说一句:整套引脚要复杂得多。在很久以前,我不得不经常处理这些复杂的引脚,所以我那时已经对故障测试盒(breakout box)⁹ 和空调制解调器(null modem)¹º 等设备非常熟悉,但在这里我不做深入探讨。当然,你们也不会想听这些细节的。)简单来说,当计算机想要使用调制解调器时(即程序打开串口时),计算机就会发送 DTR 信号;当调制解调器连接成功时,它会向计算机返回 CD 信号。如果远程端断开连接,调制解调器会停止发送 CD 信号,并将其传送回程序。对于 Unix 操作系统的程序员来说,你们现在知道 SIGHUP(Signal Hangup)信号的由来了吧!通过 DTR 信号控制通信并加以巧妙的硬件辅助,就是当时的解决方案。
虽然杜克大学率先实现了这一方案,但我认为这个想法非常巧妙,于是回到教堂山分校后,我设计了自己的版本。两个方案在细节上有所不同,但我更了解自己的版本,并且它在很多方面也更加简洁,因此我将在本文介绍我的设计。
当家用座机不使用时[即“挂机(on-hook)”状态],实际上是断开了与电话线路的连接[这并不完全准确,由于电话需要能够响铃,但响铃是交流信号,通过将电容器与振铃器串联来实现,所以电话线路对于直流线路信号来说就像是一个断路]。为了模拟挂机状态,我们将一个常态为断开的继电器串联到了电话线上。(电话公司貌似会将这种连接电话网络的方式视为不当,但我哪里会知道呢,对吧?)当计算机想要使用调制解调器时,它会发送一个信号( DTR 信号);我们连接 DTR 线路以关闭继电器,这样就可以让电话线“摘机(off-hook)”。换句话说,当计算机打开设备时(对我们来说,是/dev/ttyz7
,因为我们使用的是 DZ11 终端适配器,并将调制解调器连接到了端口 7 ),电话就会变成“摘机”状态;而当设备关闭时,电话就又会回到“挂机”状态。
那么如何拨号呢?原来,老式拨号盘电话机是通过短暂地中断线路来实现的。当你拨打一个数字,比如“3”,其实就是发送了 3 个短暂的挂机信号。这种方式叫做脉冲拨号。这些脉冲以每秒 10 次的速度发送,而且在美国,每个脉冲的闭合和断开的时间比例是 2:3。也就是说,每发出一个拨号脉冲,电话线会先断开 0.6 秒,然后再恢复连接 0.4 秒,然后才会继续下一个拨号脉冲。想要实现这个功能,其实可以通过软件来控制DTR信号。虽然我们无法完全匹配准确的时序规范(当时 Unix 系统的时钟每秒会生成 60 次中断信号),但我们可以做到接近 1:2 的比例(每 4 次中断信号对应一次挂机,每 6 次中断信号对应一次摘机),这已经足够了。(可能有些人会想是否可以通过正确的频率轻敲听筒开关来拨打电话。这种技术在设置拨号锁¹¹ 的情况下可能会有用。嗯,这是可能的。)
现在已经解决了拨号问题的硬件方面:通过软件控制 DTR 信号(利用这个继电器)可以实现摘机,并使用脉冲拨号的方式拨打所需号码。然而,我们仍然需要一个软件接口。我编写了一个驱动程序,它兼容标准的 DN11 驱动程序,但实际上与控制 DTR 线路的 DZ11 驱动程序的例程进行通信。该策略将调制解调器和拨号器视为两个独立的设备,而这也正是应用软件(如uucp)所期望的。(同样,杜克大学也率先提出了类似的解决方案。而我在北卡教堂山分校与一位优秀的电子技术人员合作完成了该项目的硬件部分。大约在一年之后,我们购置了 1200 波特的固定式调制解调器。同时,为了满足这种调制解调器更复杂的信号和时序要求,这位技术人员重新设计了拨号继电器。)
解决了自动拨号器问题后,我们又遇到了另一个难题:谁来支付电话费?当时的电话费非常昂贵,而且要根据通话距离和时间段来计算(即使在美国国内)。白天通话费用最高,晚上通话费用较低,而深夜通话费用最低。
最后达成的解决方案很简单:杜克大学有一台自动拨号器(当时为数不多的自动拨号器之一),所以他们负责拨打电话。那些希望加入我们网络的站点需要设置一个自动接听调制解调器(虽然不是很常见,但比起拨号器要常见得多),并向杜克大学支付电话费用。根据需求和流量,每晚拨打一到两次电话。文章传播的速度会比较慢,但因为杜克大学是这个网络的中心,所以大学里的人将比其他人更早看到所有新闻文章。
我们还知道这个系统并不是严格的轴辐式网络(hub-and spoke)。实际上,最初的网络由四个节点组成(duke、duke34(一台 PDP-11/34 计算机)、phs(生理系)和 unc),并且形成环路:duke-phs、duke-unc 和 phs-unc。因此我们需要一种能够处理这种环路的协议(我将在下一部分讨论)。此外,Tom Truscott 曾在贝尔实验室(Unix 就是诞生在这里)的计算机科学研究组实习过,他相信在那里工作的熟人会愿意通过电话与杜克大学联系来发送和接收数据。这个解决方案一度起了作用,但后来情况有所变化,我稍后再详细介绍。
Usenet的逻辑图(1981年4月5日),由Steven M. Bellovin教授提供
需要特别注意的是,最初的方案涉及资金往来。也就是说,管理层(即教职员工)必须对这项活动非常清楚。杜克大学和北卡来罗纳大学计算机科学系业务办公室的工作人员会看到电话账单出现突然涨幅,而杜克大学将不得不接收并处理其他站点需要支付的费用。虽然 Usenet 是一个 Skunkworks 项目¹²,但它却得到了官方认可:因为我们有一些明智的教授,他们懂得重视研究生们的创新精神。
下面要介绍的是最初的协议设计。
第四部分:文件格式
接下来更有趣的问题是,我们为什么没有使用类似电子邮件的头部格式(后来这种形式被 HTTP 所采用)。答案就是,那个时候我们中很少有人用过这些协议。几年后,我申请并收到了一本Internet Protocol Transition Workbook¹³,也是从这一刻,我对这些协议开始有所了解。而我之所以知道有这么一本工作手册,也是因为 Usenet。(再早几年,作为用户,我获得了一些关于 ARPANET 的知识,但那时我将更多精力放在了学习 Multics 操作系统上。)
所以,我们选择了简约的第 7 版 Unix。实际上,即使我们早就知道互联网(当时是 ARPANET ),我们也不会使用它。正如在后文实现部分所提到的,我们代码的第一个版本实际上是一个 shell 脚本:它将整行文本作为单一单元处理,而不是尝试解析包含任意大小写、可选空格和续行的头信息。这当然要简单得多!
下面是如何处理重复文章。显然,我们需要一个 ID 来检测重复文章。在我们的设计中,文章 ID 位于文章第一行,字母“A”之后。(注意:时间已经过去 40 年了,我已无法准确记得那次会议的具体决定。根据后文实现部分的讨论,当时存在一些实验和更改。这里我提供的信息取自 RFC 850 ¹⁴文档中的最终格式,但毫无疑问,在开发过程中肯定出现过一些变化。)
我们还希望尽量降低传输成本。正如我在前文所述,文章的传输依赖昂贵的拨号连接,发送不需要的内容会产生实际的费用。因此,文章必须包含一个已知已经阅读过该文章的系统列表。这个列表由一系列主机名组成,它们之间用叹号分隔,最后一个元素是文章发布者的登录名。比如,我(用户名为smb
)在北卡罗来纳大学教堂山分校创建的一篇文章,经过杜克大学和位于贝尔实验室研究部的计算机alice
转发后,将包含"alice!duke!unc!smb"
的路径信息。如果此路径中出现其他传输节点,则不会发送重复的副本。(是的,这也意味着某些站点很容易永远看不到某些文章。现在想来,我们当时并没有担心这个问题,甚至可能都没有注意到。)
我们为什么选择这种格式,而不是使用逗号或空格作为分隔符?因为这是uucp
转发电子邮件的格式。这样,alice
所连接的任何计算机上的用户都可以键入:
mail alice!duke!unc!smb
这封邮件会被转发到alice
和duke
,然后才能到达我所在部门的计算机,最后才到我手中。(这种电子邮件转发方式后来被证明存在问题,但我们稍后再讨论。)
如今,互联网已经全面连接,如果我们现在再来做这件事,肯定不会采用同样的方式。取而代之的是,我们会让一方向另一方发送文章 ID 列表;然后,后者会请求它尚未看过的文章。我们当时确实考虑过类似的方法,但最终放弃了。为什么?因为我们使用的是偶尔连接的拨号方式来转发文章,且重复接收到的文章数量不太可能很多。
注意:在我们最初的方案中,杜克大学每天晚上都会轮询许多站点一次。如果在通话过程中,杜克大学向它们发送文章列表,那么这些站点要等到第二天晚上才能请求这些文章,而收到文章则要等到后天晚上。这样的延迟是不能接受的。因此,我们会发送冗余文本。虽然有时确实会出现额外的传输,但我们认为在数量上是可以接受的 —— 当时还没有 JPG 和 MP3 格式,所有文章都是文本格式,因此数据量相对较小,成本也比较低。
发送日期和文章标题很直观明了,几乎没有什么讨论的必要。日期和时间行使用由库例程ctime()
¹⁵或asctime()
生成的格式。我不记得我们当时是否将日期和时间标准化为 UTC(世界标准时间),或者只是忽略了这个问题;显然,前者才是正确的做法。(这里有一个有趣的出入:一份原始公告的复制内容清楚地显示了时区,然而 RFC 和 ctime()例程却没有。我认为原始公告是正确的。)接下来我们要讨论最有趣的内容,也就是新闻组(newsgroups)的出现。
从一开始,我们就确定需要多种文章类别,也就是后来的新闻组。对于本地而言,可以根据不同需求设立多个新闻组,比如学术类(“博士论文答辩将于两周后开始”)、社交活动类(“提醒:春季野餐将在周日举行!”)等等。但是对于远程站点呢?最初的设计只有一个转发新闻组:NET
。也就是说,不会区分不同类别的非本地文章。
这个方法在当时引起了激烈的争论。争论的焦点在于:本地计算机以外值得关注的流量是否如此之少,以至于不需要进一步分类?(我们对流量的估计大错特错,进而影响了后面的几项实现决策。)最终反对意见占了上风:“如果有人要卖车怎么办?他们希望信息能发送到本地的其他计算机,而不是本地之外的其他区域。”于是我们决定,任何以“NET.”开头的新闻组内容都会被转发。不过,这也带来了一个至今没有解决的问题:我们混淆了“值得关注”和“转发范围”这两个概念。也就是说,假设站点duke
和unc
没有直接连接,而是都与alice
站点通信,那么本地值得关注的信息(两所学校仅相隔 16 公里)应该在两个站点都能看到,但根本没有必要将这种信息(例如二手车广告)发送到远在新泽西的贝尔实验室(最初四个节点之一)。(顺便提一下:几年之后,当 Usenet 已经非常普及时,有人将一则二手车广告发布到一个全球新闻组里,结果竟然收到了几位设计者的祝贺信,这让发布者非常困惑……)
还有一个更有趣的细节。从一开始,我们就意识到有些文章可能属于多个类别。因此,我们从最开始就支持将文章跨发到多个新闻组。虽然现在跨组发文章常被视为不礼貌的行为,但在最初的设计中,它确实是一个刻意加入的功能。
下面是新闻文章的最终格式,来自RFC 850:
Aeagle.642
net.general
cbosgd!mhuxj!mhuxt!eagle!jerry
Fri Nov 19 16:14:55 1982
Usenet Etiquette - Please Read
在一行空行之后,便是消息内容。
会议上我们决定的最后一件事是确定系统的名称。我们将这项技术称为“Netnews”,即网络新闻,而我们希望实现的具体实例是“Usenix”。为什么是 Usenix?维基百科(截至2019年11月17日的版本)的解释差不多是对的:“Usenet 强调了创建者们希望 Usenix 组织能在其运营中发挥积极作用。”然而,实际情况要更复杂一些。到 1979 年左右,Usenix¹⁶还一直被称作 “Unix用户组”。但贝尔实验室的律师对该名称提出了异议,认为使用了贝尔实验室的商标,因此创建者们不得不选择了一个新名称:Usenix。由于当时这些技术人员对法律缺少了解,所以对律师的做法感到很困惑。实际上,我们选择 “Usenix” 的部分原因,正是对这次被迫改名事件的温和调侃。
第五部分:实现和用户体验
想要理解我们当时一些实现的选择,要记住两件事:首先,那个时代的计算机速度非常慢。北卡罗来纳大学计算机科学系的 Unix 机甚至比当时大部分分时计算机都慢:磁盘容量小、CPU 运行速度慢,最关键的是,内存严重不足。杜克大学计算机科学系拥有性能更好的计算机 —— 他们有一台 11/70,而我们这边只有一台 11/45。由于我负责首次实现 Usenet,所以我不得不使用北卡罗来纳大学教堂山分校的计算机。(远程登录?怎么做?那时还没有互联网,两个部门都不在 ARPANET 上,拨号上网意味着要支付按分钟计费的电话费,而且很可能是白天的费率。此外,拨号连接的速度只有 300 bps,但如果我在本地,则可以通过本地 Gandalf 端口选择器实现 9600 bps 的速率。)
其次,我们明白必须通过实验来找到正确的解决方案。引用 Usenet 第一份公开公告中的话:“是的,确实存在一些问题。这个计划由几个技术爱好者合作完成。但让我们现在就按这个计划来执行吧。我们可以等网络建立起来再成立委员会。到时他们会使用这个网络,这样就能知道真正的问题所在。” 我们中没有人设计过网络协议;我们很清楚必须通过实验来尽可能地解决问题。[需要明确的是,我们都不是新手程序员,而是经验丰富的系统管理员。虽然我不了解 Tom、Jim(以及我在前文中无心漏掉的名字 Dennis Rockwell)具体有多少经验,但那时我已经积累了 14 年的编程经验,其中大部分工作与内核相关,同时也具备相当丰富的通信软件经验。]
我当初所使用的开发策略就是今天所说的“快速原型法”:我用Bourne Shell脚本实现了 Netnews 软件的第一个版本。这个脚本大约有 150 行代码,但已经具备了多个新闻组和跨组发布等功能。
为什么要使用 shell 脚本?首先,编译一个程序非常耗时,每次想尝试新功能时我都不愿意花那么多时间等待编译完成。其次,这部分代码涉及大量字符串处理,而任何一个学过 C 语言的人都知道,C 语言并不是处理字符串的最佳语言。自己写个字符串库?或许可以,但这又会进一步延长编译过程,让我陷入缓慢的编译泥潭。使用 shell 脚本能让我快速尝试新功能,并逐步完善代码。需要指出的是,shell 脚本的执行速度并不快,远远达不到实际环境的要求,但我们对此并不在意,因为它并不会在实际环境中运行。它只是一个开发原型,用于创建合适的文件格式。一旦文件格式确定下来,我就会用 C 语言重新编写代码。那段用 C 编写的代码从未公开发布过,但它的可用性要远远高于 shell 脚本版本。
遗憾的是,这份脚本(以及 C 语言版本)已经不复存在。九十年代初的时候我曾很努力地找过,但一无所获。不过,我仍然记得一些实现细节。用户订阅的新闻组列表是一个环境变量,设置在他们的 .profile
文件中:
export NETNEWS="*"
或者
export NETNEWS="NET admin social cars tri.*"
原因?它让脚本可以简单地执行如下操作
cd $NEWSHOME
groups=‘echo $NETNEWS‘
也就是说,它会输出任何与NETNEWS
环境变量中的 shell 模式匹配的目录名称。
为了查找未读文章,脚本会执行(相当于):
newsitems=‘find $groups -type f -newer $HOME/.netnews -print‘
这样就会找到自用户上次阅读新闻以来接收到的所有文章。脚本退出前会执行touch $HOME/.netnews
,将其时间标记为当前时间。(关于这方面的更多内容将在下文中提到。)
这里还有一些小技巧。如果我不希望重复显示跨组文章,脚本就会执行:
ls -tri $newsitems | sort -n | uniq
这样就列出每个文件的“i-node”编号,并删除重复的所有副本,只保留第一个:跨组文章以单个文件形式被链接到多个目录中。这种简单的方法不仅可以找到重复文章,还节省了磁盘空间(当时磁盘空间非常昂贵)。(熟悉 Unix shell 编程的人可能会迫不及待地告诉我,上面提到的uniq
命令并不能完全实现我所说的功能。没错。我会如何处理这个问题(我确实处理了)就留给读者作为练习。为了确保你的解决方案有效,请务必只使用我在 1979 年可以使用的 shell 命令¹⁷。)
我在前文提到过,脚本只会在成功退出时更新上次阅读的时间。是的,这个版本无法按顺序阅读,也不能跳过文章稍后再看,甚至不能在一天的新闻 feed 阅读过程中中途停止,否则会重新看到所有刚刚读过的内容。听起来很荒唐,但这是因为我犯了一个非常可笑的错误:我预测 Usenet 的最大流量永远不会超过每天 1-2 篇文章。而如今,Usenet 的流量似乎每天都超过 60 太字节¹⁸,每天的发帖数量超过 1 亿。正如我去年所说¹⁹,Usenet 如此成功,以至于超出了我当初所预测的流量那么多数量级,我为有机会共同创建它而感到自豪。
这里还有一些值得注意的细节。每条消息的第一行都包含文章 ID:站点名、一个句点和一个序列号。这确保了唯一性:每个站点都会对自己的文章进行编号,无需全局协调。[这里有一个默许的假设,即每个站点都拥有一个独特的名称。总体来看,这种做法是对的(虽然系统管理员很喜欢使用科幻小说中的名字和地点而导致重名的发生,不过通常不会发生在组成 Usenet 的计算机之间)。]我们还使用文章 ID 作为存储文件的名称。但这个决策却暗含一个限制。根据我的回忆,站点名称被限制在 8 个字符以内; 而当时的文件名限制在 14 个字符以内。这意味着序列号最多只能是五位数,对吗?不完全对。“站点名.序列号(site.sequence)”格式是一种传输格式,将其用作文件名是一种实现决策。如果有必要,我可以轻松地为每个站点创建一个单独的目录。考虑到我当时对流量的低预期,这么做显然没有必要,尤其对于一个原型程序来说。
我们仅使用文章 ID 作为文件名,并依靠文件的存在及其最后修改时间作为文章的唯一元数据,这种做法其实还有另一个更微妙的原因。建立数据库显然是一个更加强大和灵活的选项。然而,要在系统上拥有所有新闻条目的单一数据库就需要某种锁定机制来避免竞争,而在当时使用的第 7 版 Unix 上实现锁定并非易事。除了通过管道(pipes)之外,当时也没有实现进程间通信的机制,但管道仅对来自同一父进程的子进程有用,这些父进程必须在创建子进程之前创建管道。所以我们选择依赖文件系统,它拥有自己的内部锁定机制来保证数据的一致。这种方法确实有一些缺点,不过如果你每天只接收 1-2 篇文章,它们就微不足道了。
创建者的路径有两个作用。第一,它告诉我们哪些站点不需要再接收新文章的副本。第二,它实际上也是一个有效的uucp
电子邮件地址;这意味着可以通过邮件回复发帖者。
我曾注意到公告中的日期格式与 RFC 850 中的日期格式存在差异。经过进一步思考,我现在认为公告才是正确的,RFC 是错误的。原因?公告中显示的格式正是date
命令输出的格式;在 shell 脚本中,这个字符串非常容易使用,而删除时区信息则很麻烦。我想我当初不会费力删除一些显然很重要的东西。
Usenet 的用户界面被设计成与第 7 版 Unix 的mail
²º命令界面类似。它简单易用,在处理少量邮件的情况下表现很好。我们还推测,几乎所有的 Usenet 读者都将熟悉这个界面。
为了将文章发送到邻近系统,我们使用了uucp
的远程执行组件。
uux $dest_site!rnews <$article_file_name
也就是说,在远程系统(其名称存储在 shell 变量dest_site
中)上,脚本会将首次传输的指定文件作为标准输入,然后执行rnews
命令。但前提是,接收站点必须允许执行rnews
命令,而这又要求他们重新编译uucp
(配置文件?哪些配置文件?)。这对有些人来说会是一个难题,我将在第六部分介绍。
《Usenet的早期历史(上)》已结束。
The Nexus
注释:
虽然 DAA 现在描述的是连接到电话网络的设备的一个组成部分,但在 20 世纪 60 年代和 70 年代,它描述的是贝尔系统强制要求的单独设备,连接在电话线和非贝尔设备(通常是调制解调器)之间。(审校者注)
Gandalf Technologies是一家加拿大数据通信公司(于1997年破产),成立于1971年,总部位于渥太华。该公司以其调制解调器和终端适配器而闻名,这些设备允许计算机终端通过单个接口连接到多个主机计算机。
硬拷贝终端是指早期连接到计算机的机电电传打字机(TeleTypewriter, TTY),如最初用于电报的Teletype Model 33。早期的电传打字机通常配置有键盘发送-接收(KSR)或自动发送-接收(ASR)。某些型号(如ASR Teletype)包括纸带阅读器和打孔器,可以记录程序列表等输出。这些设备可以通过纸带阅读器将纸带上的数据重新输入计算机,或将数据打印到纸上。它们使用的是在电报中广泛应用的电流环路接口。电传打字机可以使用较便宜的只读(RO)配置,这时候并没有使用屏幕,所以是真正的“打印”输出而非在屏幕上的“显示”。(审校者注)
Tom Truscott 和 Jim Ellis 是 Usenet 的共同创始人。1979年,作为杜克大学的研究生,他们开发了一个分布式网络系统,旨在使计算机科学研究人员能够在远程计算机之间交换消息和文件。这一系统后来被称为 Usenet,它利用 UUCP(Unix-to-Unix Copy Protocol)在 Unix 系统之间传输数据,成为早期互联网的重要组成部分。Usenet 不仅促进了全球范围内的信息交流,还对后来电子邮件和网络论坛的发展产生了深远影响。
Hayes智能调整解调器是由Hayes Microcomputer Products推出的一款具有划时代意义的调制解调器。它采用内置的“Hayes命令集”(AT命令),使得计算机可以通过简单的命令控制调制解调器进行拨号和数据传输,从而显著简化了计算机与调制解调器的通信过程,并成为了行业标准。这一创新极大地推动了个人计算机网络通信的发展,使得远程数据传输变得更加便捷和普及。
声学耦合器是一种用于在电子设备之间传输声音信号的装置。它通常由两个部分组成:一个发送器和一个接收器。发送器将电子信号转换为声波信号,并通过声波传输到接收器,接收器再将声波信号转换回电子信号。声学耦合器通常用于计算机、电话系统和其他电子设备之间的数据传输。
https://en.wikipedia.org/wiki/Hush-A-Phone_Corp._v._United_States
RS-232 标准是美国电子工业协会(EIA)制定的用于数据终端设备(DTE)和数据通信设备(DCE)之间串行二进制数据交换的接口标准。RS-232标准广泛应用于计算机、打印机、工业控制设备等各类电子设备之间的数据通信,是目前应用最为广泛的串行接口标准之一。
breakout box是一种电子测试设备,广泛应用于系统、子系统和组件级别的集成测试、维护和故障排除。
null modem通信是指使用交叉 RS-232 电缆直接将电传打字机连接在一起,而无需使用调制解调器。它也用于通过串行连接将计算机连接到打印机,因为两者都是数据终端设备(DTE)。
https://www.pinterest.com/pin/269653096409174381/
Skunkworks 是指由小型、独立团队进行的高度创新和保密的研发项目。这种项目通常绕过常规的企业流程,以快速开发和测试新技术或产品。这个术语起源于美国洛克希德公司的一个高度机密的先进航空航天和国防项目 Skunk Works。该项目成立于 1943 年,以快速开发创新技术和产品而闻名。(审校者注)
https://www.computerhistory.org/collections/catalog/102714750
https://www.rfc-editor.org/rfc/rfc850.html
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man3/ctime.3
USENIX是一个致力于先进计算系统研究和开发的非营利性组织。成立于1975年,USENIX 在计算机科学领域尤其是操作系统、网络和安全方面具有重要影响力。
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man1
https://en.wikipedia.org/w/index.php?title=Usenet⦁&⦁oldid=924251117
https://www.cs.columbia.edu/~smb/blog/2018-02/2018-02-23.html
https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man1/mail.1
作者简介:
Steven M. Bellovin 是哥伦比亚大学计算机科学系教授,专注于安全、隐私及公共政策研究。Bellovin 在贝尔实验室和 AT&T 实验室研究所(担任AT&T Fellow)工作多年后,于2005年加入哥伦比亚大学。他在研究生期间共同创立了 Usenet,并因此与 Tom Truscott 和 Jim Ellis 一起获得了 1995 年 Usenix 终身成就奖。2023 年,他与 Matt Blaze 和 Susan Landau 一起因在计算机安全和隐私方面的公共政策工作再次获得该奖。Bellovin 还获得了 2007 年 NIST/NSA 国家计算机系统安全奖,并入选了网络安全名人堂。他曾担任联邦贸易委员会首席技术官以及隐私和公民自由监督委员会的技术学者。
他是美国国家工程院院士,曾任美国国家科学、工程和医学院计算机科学和电信委员会委员。他著有Thinking Security(国内出版名为《阻击黑客:技术、策略与案例》)并合著Firewalls and Internet Security: Repelling the Wily Hacker。
原文链接:
https://www.cs.columbia.edu/~smb/blog/2019-11/2019-11-14.html
致谢:
感谢赵军老师对本文的审校。