1. 一台四十多年前的机器,怎么就成了我的Web服务器
先说说这个标题到底在讲什么。PDP-11/83 是DEC公司在1980年代中期推出的一款16位小型机,属于PDP-11系列里比较晚期的型号,CPU是KDJ11-B,跑的是211BSD——这是2.11BSD的简称,一款基于UNIX System V早期版本衍生出来的BSD分支,专门为PDP-11这种内存寻址能力有限的机器做了大量裁剪和优化。而“serving this page”的意思很直白:这台机器现在正儿八经地跑着一个HTTP服务,你访问的那个页面,就是从这台四十多年前的硬件上吐出来的。
我第一次看到这个项目的时候,第一反应是“这人是不是闲的”。但仔细一想,这件事的价值恰恰在于它的“不划算”。现在随便一台树莓派Zero都能秒杀PDP-11的算力,功耗还不到它的零头。可正是因为它不划算,才逼着你去理解每一个字节是怎么从磁盘读到内存、怎么经过TCP/IP栈、怎么被一个1970年代设计的操作系统调度到网卡上的。这种理解,你在现代Linux上跑一万遍Nginx也得不到。
这篇文章适合谁看?如果你手里正好有一台复古机器想让它“活”起来,或者你对UNIX的早期实现有好奇心,再或者你只是想看看在没有systemd、没有动态链接、甚至没有虚拟内存的完整支持的条件下,一个网络服务是怎么跑起来的,那这篇内容应该能给你不少参考。我会把整个恢复过程、211BSD的安装配置、网络栈的调试、HTTP服务的搭建,以及我踩过的那些坑,尽量完整地讲一遍。
需要提前说明的是,PDP-11/83的具体硬件配置在不同批次间有差异,我手上这台是KDJ11-B CPU、4MB内存、两个RD54硬盘(各159MB)、一块DELQA以太网卡。如果你手上的配置不同,部分参数需要相应调整,但整体思路是通用的。
2. 为什么选211BSD而不是其他系统
2.1 PDP-11上能跑的操作系统有哪些
PDP-11这个平台生命周期极长,从1970年一直生产到1990年代,期间DEC自己就发布过RSX-11、RSTS/E、RT-11、IAS等多个操作系统,第三方还有UNIX V6、V7、2.9BSD、2.11BSD等。如果你只是想让机器跑起来,选择其实不少。
但如果你目标是“跑一个网络服务”,筛选条件就变得很苛刻了。RT-11是实时系统,没有内存保护,TCP/IP栈基本没有;RSX-11M+虽然有网络能力,但DECnet协议居多,TCP/IP需要额外购买;UNIX V6太老,网络代码不完整;V7虽然经典,但对PDP-11/83的硬件支持不够完善,特别是大内存和较新的磁盘控制器。
2.11BSD是我认为最平衡的选择。它在2.9BSD的基础上做了大量改进,支持分离的I/D空间(PDP-11/83的KDJ11-B支持这个特性),能利用更大的物理内存,TCP/IP栈是BSD的标准实现,虽然老但完整。更重要的是,2.11BSD的源码和安装介质在互联网上相对容易找到,社区里还有人在维护补丁。
2.2 211BSD的版本选择和获取
2.11BSD的最后一个官方版本是2.11BSD Patch Level 431,之后社区有人继续维护到Patch Level 445左右。我建议直接用最新的patch level,因为早期的版本在TCP/IP栈上有一些已知的问题,比如处理窗口缩放时的bug,在高延迟网络下会出问题。
获取途径主要是通过模拟器社区和复古计算论坛。通常你会拿到一组磁盘镜像文件,可能是.tap格式(用于模拟器)或者物理磁盘的dd镜像。如果你打算在真实硬件上安装,需要把这些镜像写到SCSI硬盘或者DEC的RD系列硬盘上。我这边用的是SCSI转接方案,因为RD54硬盘现在基本找不到完好的了。
注意:2.11BSD的安装介质通常是以“boot tape”形式分发的,但PDP-11/83的ROM里自带的boot程序可能不支持直接从SCSI磁带启动。你需要先通过其他方式(比如从RK05或RL02软盘)引导一个最小的安装环境,再从磁带读取完整系统。
2.3 为什么不用模拟器
这个问题我被问过很多次。用SIMH模拟一个PDP-11,跑2.11BSD,再配个虚拟网卡,确实能省掉大量硬件调试的时间。但模拟器有个根本问题:它把硬件的不确定性屏蔽掉了。真实机器上,你可能会遇到内存校验错误、磁盘控制器超时、网卡DMA冲突,这些在模拟器里永远不会出现。而正是这些问题的排查过程,让你真正理解这套系统的运作方式。
另外,模拟器的时序和真实硬件不同。2.11BSD里有些驱动代码对时序有隐含假设,在模拟器上跑得好好的,到真机上可能就挂。我遇到过DELQA网卡在模拟器里工作正常,但在真机上因为中断响应延迟导致丢包的情况。这种问题不摸真机是发现不了的。
3. 硬件恢复:从通电到能引导
3.1 电源和背板检查
PDP-11/83用的是BA23或BA123机箱,电源是H7864或类似的型号。这些电源到现在已经三四十年了,电解电容基本都干涸了。我的建议是,在第一次通电之前,先把电源拆出来,检查所有电解电容有没有鼓包或漏液。如果有条件,直接换掉所有电解电容,特别是初级侧的滤波电容。
背板方面,Q22总线的背板一般比较可靠,但插槽的簧片可能会氧化。用电子接触清洁剂喷一下,然后反复插拔几次板卡,确保接触良好。我遇到过因为背板接触不良导致CPU随机取指错误的情况,排查了很久才发现是插槽问题。
3.2 磁盘子系统的选择
原装的RD54硬盘是MFM接口的,容量159MB,现在能找到的基本都是报废品。我的方案是用SCSI硬盘加SCSI到Q22总线的转接卡,比如CMD CQD-220或者Emulex UC07。这些卡在eBay上偶尔能买到,但价格被炒得很高。
另一个方案是用SCSI2SD或者ZuluSCSI这样的固态存储模拟器,把SCSI硬盘替换成SD卡。这个方案的好处是可靠性高、功耗低、没有机械磨损。但要注意,2.11BSD的磁盘驱动可能不认识这些模拟器报告的几何参数,需要在驱动里手动指定或者用工具修改分区表。
我最终用的是ZuluSCSI接一张8GB的SD卡,只用了前200MB左右给2.11BSD。分区方案是:
| 分区 | 用途 | 大小 | 文件系统 |
|---|---|---|---|
| a | 根分区 | 16MB | 2.11BSD FFS |
| b | 交换分区 | 16MB | swap |
| c | 整个磁盘 | 200MB | 无 |
| d | /usr | 80MB | 2.11BSD FFS |
| e | /home | 40MB | 2.11BSD FFS |
| f | /var | 20MB | 2.11BSD FFS |
这个分区方案是2.11BSD安装文档里推荐的,我根据自己的需求调整了/usr的大小,因为要装编译器和网络工具。
3.3 内存和CPU板
KDJ11-B CPU板上的PROM里包含了boot程序,支持从多种设备引导。如果你要改引导设备,需要进入CPU的微码控制台(通过前面板的开关或者串口),修改boot设备列表。具体操作是按住AUX开关,然后输入设备代码。PDP-11/83的boot设备代码表在CPU板的标签上通常有印。
内存方面,PDP-11/83最大支持4MB物理内存,但2.11BSD只能直接寻址到2MB左右,剩下的需要通过分离I/D空间和bank switching来利用。我的机器装了4MB,2.11BSD识别出大约3.5MB可用,剩下的被内核保留做缓冲。
实操心得:如果你在真机上装2.11BSD,第一次引导时如果看到“panic: cannot mount root”之类的错误,先检查内存板有没有插紧。PDP-11的内存板金手指容易氧化,用橡皮擦擦一下再插。
4. 211BSD安装:从磁带引导到系统可用
4.1 制作安装介质
如果你用的是ZuluSCSI,需要把2.11BSD的安装镜像转换成ZuluSCSI能识别的格式。通常安装镜像是一个.tap文件,里面包含了多个文件系统的转储。你需要用工具(比如tap2scsi或者自己写脚本)把它转换成磁盘镜像,然后写到SD卡上。
具体步骤:
- 下载2.11BSD的安装磁带镜像,通常是
2.11BSD_install.tap之类的文件名。 - 用
tap2scsi工具把tap文件转换成SCSI磁盘镜像。这个工具在复古计算社区有编译好的版本。 - 把生成的镜像写到SD卡上,注意偏移量要正确。ZuluSCSI默认从LBA 0开始读取,所以镜像要直接dd到SD卡的开头。
- 把SD卡插到ZuluSCSI上,连接到PDP-11的SCSI总线。
如果你用的是真实磁带机,那就简单了,直接把磁带塞进去,从磁带引导就行。但磁带机本身可能也需要维护,比如清洁磁头、调整张力。
4.2 引导和安装过程
从磁带引导后,你会进入一个独立的安装环境,通常是一个内存文件系统。安装脚本会引导你完成分区、创建文件系统、从磁带解压文件等步骤。
关键步骤:
- 分区:用
disklabel命令编辑磁盘标签。注意2.11BSD的磁盘标签格式和现代BSD不同,分区偏移量是以512字节为单位计算的。 - 创建文件系统:用
mkfs命令,参数是mkfs /dev/rd0a之类的。2.11BSD的mkfs参数和现代版本不同,需要指定文件系统类型和inode数量。 - 从磁带解压:用
restore命令从磁带设备读取转储文件。注意磁带设备名可能是/dev/rmt0或/dev/nrmt0,取决于是否要倒带。
整个安装过程大概需要30到60分钟,取决于磁盘速度。ZuluSCSI比真实硬盘快很多,大概20分钟就能完成。
4.3 首次启动和基本配置
安装完成后,从硬盘引导。第一次启动时,系统会问你一些基本配置问题,比如时区、主机名、网络配置等。2.11BSD的启动脚本在/etc/rc和/etc/rc.local里,你可以手动编辑。
网络配置需要编辑/etc/hosts、/etc/netstart和/etc/rc.conf(如果有的话)。2.11BSD的网络配置比较原始,通常是在/etc/rc.local里直接调用ifconfig和route命令。
我的配置大概是这样的:
# /etc/rc.local 片段 ifconfig de0 192.168.1.100 netmask 255.255.255.0 route add default 192.168.1.1de0是DELQA网卡的设备名。如果你用的是其他网卡,设备名可能不同,比如qe0是DEQNA,xp0是XNA等。
注意:2.11BSD的
ifconfig命令不支持现代的参数格式,比如inet关键字。你直接写IP地址就行,不要加额外的关键字。
5. 网络栈调试:让TCP/IP真正跑起来
5.1 DELQA网卡的驱动问题
DELQA是DEC的以太网卡,基于AMD LANCE芯片组。2.11BSD的de驱动支持这块卡,但有一些已知的问题。最常见的是中断丢失和DMA溢出。
中断丢失的表现是:网卡能收到包,但系统不响应,ping不通。排查方法是看netstat -i的输出,如果Ipkts在增加但Idrop也在增加,说明中断处理有问题。
DMA溢出的表现是:大包传输时系统崩溃或者数据损坏。这是因为PDP-11的Q22总线DMA能力有限,LANCE芯片在高速传输时可能超出总线的响应能力。
解决方法:
- 在驱动里降低LANCE的DMA突发长度。2.11BSD的
de驱动有一个DE_DMA_BURST之类的宏,可以改成较小的值。 - 如果中断丢失,检查网卡的中断向量和CPU的中断优先级设置。PDP-11/83的中断优先级是固定的,但你可以调整网卡在背板上的插槽位置来改变优先级。
5.2 TCP/IP参数调优
2.11BSD的TCP/IP栈是BSD 4.3的早期版本,默认参数是为低速网络设计的。在现代网络上跑,需要调整一些参数。
关键参数在/sys/netinet/tcp.h和/sys/netinet/in.h里,需要重新编译内核。我调整了以下几个:
TCP_MSS:默认是512,我改成了1024。PDP-11的内存有限,MSS太大会导致分片,太小会影响吞吐。TCP_SNDSPACE和TCP_RCVSPACE:默认各是4096,我改成了8192。现代网络的带宽延迟积比1980年代大得多,缓冲区太小会导致吞吐上不去。TCP_TTL:默认是60,我改成了64,和现代系统一致。
重新编译内核的步骤:
cd /sys/conf cp GENERIC MYCONF # 编辑MYCONF,修改参数 config MYCONF cd ../MYCONF make depend make编译过程在PDP-11/83上大概需要20到30分钟。编译完成后,用cp命令把新内核复制到根目录,然后重启。
5.3 网络性能实测
调整完参数后,我用ftp和http做了简单的吞吐测试。从一台现代Linux机器上通过HTTP下载一个1MB的文件,PDP-11/83的吞吐大概在80到120KB/s之间。这个速度受限于CPU的处理能力和内存带宽,不是网卡的问题。
ping的延迟在局域网内大概是2到5毫秒,比现代机器高一个数量级,但考虑到CPU主频只有十几MHz,这个延迟是合理的。
实操心得:如果你发现网络吞吐远低于预期,先检查网卡的DMA设置。DELQA有一个跳线可以设置DMA模式,默认是“block mode”,改成“demand mode”可能会提高稳定性,但会降低吞吐。我试过两种模式,block mode在轻负载下更快,但重负载下容易出错。
6. 搭建HTTP服务:从零写一个能用的Web服务器
6.1 为什么不用现成的Web服务器
2.11BSD的软件仓库里有一些老版本的Web服务器,比如NCSA HTTPd的早期版本,或者CERN httpd。但这些软件要么依赖现代C编译器特性,要么需要大量内存,在PDP-11上跑起来很吃力。
我决定自己写一个极简的HTTP服务器。需求很简单:监听80端口,接受GET请求,返回静态文件,支持基本的MIME类型。不需要CGI,不需要SSL,不需要虚拟主机。
用C写,大概200行代码。编译用2.11BSD自带的cc,这是K&R C的编译器,不支持ANSI C的一些特性,比如函数原型和const关键字。写代码的时候要注意。
6.2 代码实现要点
核心逻辑:
- 创建socket,绑定80端口,监听。
- 接受连接,读取请求行。
- 解析请求行,提取方法和路径。
- 打开文件,读取内容,构造响应。
- 发送响应,关闭连接。
关键代码片段:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> main() { int sock, msgsock, len; struct sockaddr_in server; char buf[1024]; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("opening stream socket"); exit(1); } server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(80); if (bind(sock, &server, sizeof(server)) < 0) { perror("binding stream socket"); exit(1); } listen(sock, 5); for (;;) { msgsock = accept(sock, 0, 0); if (msgsock == -1) perror("accept"); else { len = read(msgsock, buf, sizeof(buf)); if (len > 0) { buf[len] = '\0'; handle_request(msgsock, buf); } close(msgsock); } } }handle_request函数负责解析请求和发送响应。注意2.11BSD的read和write系统调用和现代UNIX基本一致,但send和recv可能不存在,要用read和write代替。
6.3 编译和部署
编译命令:
cc -o httpd httpd.c如果报错说找不到socket或bind,检查/usr/include/sys/socket.h和/usr/include/netinet/in.h是否存在。2.11BSD的安装可能没有包含完整的网络头文件,需要从源码里复制。
部署:
- 把
httpd复制到/usr/local/bin。 - 创建
/var/www目录,放静态文件。 - 在
/etc/rc.local里加上/usr/local/bin/httpd &,让系统启动时自动运行。
注意:2.11BSD没有
nohup命令,如果你从终端启动httpd,退出终端时进程会被杀掉。用&放到后台也不行,因为shell退出时会发送SIGHUP。解决办法是用setsid或者写一个简单的daemonize函数。我是在代码里直接调用fork和setsid,让进程脱离终端。
6.4 性能和安全考虑
这个极简HTTP服务器没有任何安全防护。没有路径遍历检查,没有请求大小限制,没有超时处理。如果暴露在公网上,分分钟被搞挂。
我的做法是:
- 只监听局域网IP,不监听公网。
- 在路由器上做端口转发,只转发特定端口到PDP-11。
- 用
inetd或者tcpd做访问控制,只允许特定IP连接。
如果你真的想把它暴露到公网,至少加上:
- 请求行长度限制(比如1024字节)。
- 路径检查,拒绝包含
..的请求。 - 超时处理,用
alarm或者select设置超时。 - 限制并发连接数。
但这些在2.11BSD上实现起来都比较麻烦,因为系统调用和库函数都比较原始。我的建议是,如果只是自己玩,局域网内跑跑就行了。
7. 常见问题与排查技巧实录
7.1 引导失败
现象:开机后CPU板上的LED显示错误码,或者串口没有任何输出。
排查思路:
- 检查电源输出电压是否正常。PDP-11/83需要+5V、+12V、-12V,用万用表量一下。
- 检查CPU板的PROM是否插紧。PROM芯片容易因为热胀冷缩而接触不良。
- 检查内存板。如果有多块内存板,试着只插一块,排除内存故障。
- 检查背板插槽。用已知良好的板卡替换测试。
常见错误码:
| 错误码 | 含义 | 可能原因 |
|---|---|---|
| 000001 | 电源故障 | 电源输出电压异常 |
| 000002 | CPU故障 | CPU板或PROM问题 |
| 000004 | 内存故障 | 内存板故障或接触不良 |
| 000010 | 总线故障 | 背板或插槽问题 |
7.2 网络不通
现象:ifconfig显示网卡已配置,但ping不通。
排查步骤:
- 检查网线。PDP-11/83的DELQA是AUI接口,需要外接收发器。收发器故障是常见原因。
- 检查
netstat -i的输出。如果Ipkts为0,说明网卡没收到包,可能是收发器或网线问题。 - 检查中断。用
vmstat -i看中断计数是否在增加。如果不增加,说明中断没触发,可能是网卡的中断向量设置不对。 - 检查路由。
netstat -r看默认路由是否正确。 - 检查ARP。
arp -a看ARP表是否有条目。如果没有,说明ARP请求没发出去或者没收到响应。
我踩过的坑:DELQA网卡有一个“heartbeat”跳线,默认是开启的。这个功能会定期发送心跳包,但在某些网络设备上会被误认为是环路,导致端口被关闭。我遇到过交换机因为收到DELQA的心跳包而把端口disable的情况。解决办法是关掉heartbeat跳线。
7.3 系统崩溃
现象:系统随机panic,错误信息可能是“panic: trap”或“panic: segmentation violation”。
排查思路:
- 检查内存。PDP-11的内存板容易出问题,用内存测试程序跑一遍。2.11BSD的安装介质里通常有一个
memtest程序。 - 检查内核配置。如果你改过内核参数,可能是参数不兼容导致的。恢复默认配置试试。
- 检查磁盘。磁盘错误可能导致内核读取错误的代码或数据。用
fsck检查文件系统。 - 检查电源。电压不稳会导致CPU随机出错。用示波器看+5V的纹波。
我的经验:PDP-11/83对电源质量很敏感。我的机器一开始随机崩溃,换了电源里的滤波电容后就稳定了。如果你不想换电容,至少加一个UPS,确保输入电压稳定。
7.4 HTTP服务无响应
现象:httpd进程在运行,但浏览器连接超时。
排查步骤:
- 用
telnet从另一台机器连接PDP-11的80端口,看是否能建立TCP连接。如果连不上,说明网络或防火墙问题。 - 如果TCP连接能建立但没响应,说明
httpd卡住了。用ps看进程状态,如果是D状态(不可中断睡眠),可能是磁盘I/O问题。 - 检查
httpd的日志。我在代码里加了简单的日志输出,写到/var/log/httpd.log。看日志里有没有错误信息。 - 检查文件权限。
httpd进程的用户ID可能没有权限读取/var/www下的文件。2.11BSD的权限模型和现代UNIX基本一致,用chmod和chown调整。
常见问题速查表:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 连接被拒绝 | httpd没运行或端口没监听 | 检查进程和netstat输出 |
| 连接超时 | 网络不通或防火墙阻挡 | 检查网线和路由 |
| 返回403 | 文件权限不足 | chmod 644文件,chmod 755目录 |
| 返回404 | 文件路径错误 | 检查请求路径和实际文件路径 |
| 返回500 | 服务器内部错误 | 检查httpd日志 |
| 传输中断 | 内存不足或DMA冲突 | 减小缓冲区,调整DMA设置 |
7.5 磁盘空间不足
2.11BSD的根分区只有16MB,很容易满。/usr分区80MB,装完编译器和网络工具后也剩不了多少。
清理技巧:
- 删除
/usr/src里的源码,如果你不需要重新编译内核的话。 - 删除
/usr/share里的文档和man page。 - 用
compress压缩日志文件。 - 把不常用的文件移到
/home分区。
我的做法:我把/usr/src移到了/home/src,然后在/usr里做了一个符号链接。这样根分区和/usr分区都能保持足够的空闲空间。
8. 这套系统还能怎么玩
8.1 跑一个简单的BBS
2.11BSD上可以跑老版本的BBS软件,比如Citadel或者WWIV的UNIX版本。这些软件对内存和磁盘的要求不高,PDP-11/83完全能胜任。你可以通过telnet或者串口拨号接入,体验1980年代的在线社区。
配置BBS需要:
- 一个支持多用户的终端驱动。2.11BSD的
/etc/ttys里可以配置串口终端。 - 一个BBS软件包。Citadel的源码在复古计算社区有存档。
- 足够的磁盘空间。BBS的消息库会不断增长,建议放在
/home分区。
8.2 做一台DNS服务器
2.11BSD自带named,这是BIND的早期版本。虽然功能有限,但做一台局域网DNS服务器足够了。配置/etc/named.boot和区域文件,然后在/etc/rc.local里启动named。
注意:老版本的BIND有一些已知的安全漏洞,不要暴露在公网上。局域网内用没问题。
8.3 跑UUCP
UUCP是UNIX到UNIX的复制协议,在1980年代很流行。2.11BSD自带UUCP工具,你可以用它和其他的复古UNIX系统交换文件或邮件。配置/usr/lib/uucp/L.sys和/usr/lib/uucp/L-devices,然后启动uucico。
UUCP可以通过串口或者TCP/IP运行。通过TCP/IP的话,需要配置/etc/inetd.conf里的uucpd服务。
8.4 连接现代网络
PDP-11/83的TCP/IP栈虽然老,但基本功能是完整的。你可以用它访问现代的HTTP服务器(虽然很多网站会因为TLS要求而拒绝连接),或者用ftp下载文件。
如果你想让PDP-11/83访问现代网络,有几个限制:
- 不支持TLS。所以HTTPS网站基本访问不了。
- 不支持IPv6。
- TCP窗口缩放不支持,高延迟网络下吞吐很低。
- DNS解析需要手动配置,2.11BSD的
resolv.conf格式和现代版本略有不同。
我的做法是在局域网里放一台现代Linux机器做代理,PDP-11/83通过代理访问外部网络。代理可以用squid或者简单的socat转发。
9. 一些个人体会
这台PDP-11/83从到手到能跑HTTP服务,前后花了大概三个月。大部分时间不是在装系统,而是在修硬件。电源换了电容,内存板换了插槽,网卡换了收发器,硬盘换成了ZuluSCSI。每一次故障都是一次学习,让你更理解这台机器的设计哲学。
2.11BSD这个系统,用现代的眼光看,到处都是限制。内存小、磁盘慢、没有虚拟内存、没有动态链接、没有多线程。但正是这些限制,逼着你去优化每一行代码,去理解每一个系统调用的开销。我写那个HTTP服务器的时候,为了减少一次read调用,把请求行和头部一次性读进来,而不是分两次读。这种优化在现代系统上毫无意义,但在PDP-11上,每一次系统调用都是几千个时钟周期。
如果你也想搞一台复古机器跑网络服务,我的建议是:先从模拟器开始,熟悉系统和工具链,然后再上真机。真机的调试成本很高,没有模拟器的基础,很容易卡在硬件问题上。另外,准备好足够的备件,特别是电源和内存。这些机器的年龄比很多读者的年龄都大,故障是常态,不是例外。
最后分享一个小技巧:2.11BSD的/etc/rc.local里可以放任何你想在启动时运行的命令。如果你想让HTTP服务在启动时自动运行,除了加httpd &之外,还要确保httpd进程不会因为终端关闭而被杀掉。我的做法是在httpd代码里调用fork和setsid,让它成为一个真正的daemon。如果你不想改代码,可以用/usr/lib/daemon或者自己写一个简单的wrapper脚本。
这个项目后续还可以这样扩展:把HTTP服务改成支持CGI,这样就能跑一些动态页面;或者加一个简单的SMTP服务器,让PDP-11/83能收发邮件;再或者把系统做成一个复古计算的教学平台,让更多人体验早期UNIX的魅力。