RFC 7 带读:Host-IMP Interface——从用户请求到硬件中断,ARPANET 主机网络栈的早期分层
前言
读完 RFC 6 以后,Host 与 IMP 之间“到底应该怎么接”仍然只是一些散落的问题:
- 字符转换由谁完成;
- Host 应该向 IMP 提供哪些控制信息;
- IMP 应该怎样反馈状态;
- Link 字段到底多大;
- 出错以后谁负责处理。
到了 RFC 7,这些讨论第一次明显地收敛成一份软件组织设计草案。
RFC 7 的标题非常直接:
Host-IMP Interface。
作者 Gérard Deloche 没有试图重新定义整个 ARPANET,而是盯住 UCLA Host 这一侧,回答一个更接近系统软件实现的问题:
一个用户程序想通过 ARPANET 发数据时,Host 操作系统内部到底应该有哪些程序、数据结构和硬件接口,把这个请求一路送到 IMP?
RFC 7 给出的核心结构只有两个主要程序:
1 | |
其中:
- **Network Program(网络程序)**面向用户请求,负责多路复用、消息封装、长度检查、分片、字符转换和缓冲;
- **Handler Program(处理程序)**面向硬件,运行在特权模式,负责驱动通道设备、处理中断、搬运缓冲区和维护接口状态。
如果用今天的词汇做一个谨慎的类比:
1 | |
但这只能帮助理解。
RFC 7 不是 Linux 网络栈,也不是现代 socket + driver 架构的直接原型。它面对的是 1969 年 UCLA 的 SDS Sigma 7 和一台即将接入 ARPANET 的 IMP。
更重要的是,RFC 7 自己就明确说它只是:
preliminary software design(初步软件设计)。
而且它的最后一章不是“Conclusion”,而是:
Questions。
其中还留着几个非常关键、尚未解决的问题:
1 | |
所以 RFC 7 最值得今天的软件开发者阅读的地方,并不是记住一个 16-bit Header。
而是观察:
一个网络协议概念,怎样开始变成操作系统里的程序、队列、缓冲区、系统调用、特权代码和硬件中断。
RFC 基本信息
| 项目 | 内容 |
|---|---|
| RFC | RFC 7 |
| 官方英文标题 | Host-IMP interface |
| 作者 | G. Deloche(Gérard Deloche) |
| 所属机构 | University of California at Los Angeles(UCLA) |
| 文档日期 | May 1969 |
| RFC 100 记录日期 | 5 May 1969 |
| 研究对象 | ARPANET Host-IMP Interface Programs |
| 参考基础 | BBN Report No. 763 |
| RFC Editor 当前状态 | Unknown |
| 1971 年历史分类 | Obsolete |
| RFC Editor 信息页 | RFC 7: Host-IMP interface |
| RFC 原文 | RFC 7 原文 |
这是一份“抢救式重建”的历史文档
RFC 7 有一个必须先知道的档案问题。
RFC Editor 在正文开头专门说明:
- 原始 RFC 7 是手写稿;
- 留存下来的副本部分字迹已经无法辨认;
- 后来 SRI 的 **Augmentation Research Center(ARC)**在 NLS 中重新录入;
- 今天看到的机器可读文本,是基于现有材料做出的最佳重建。
因此正文中可以看到:
1 | |
之类的标记。
这意味着 RFC 7 与现代 RFC 有根本不同:
1 | |
所以阅读过程中,如果发现:
- 拼写错误;
- 图形错位;
- 某些句子缺字;
- 术语前后略有不一致;
首先要考虑的是:
这可能是历史文档保存问题,而不是某个已经精确定义的协议细节。
RFC 7 很快就过时了
1971 年的 RFC 100 对 RFC 7 的记录非常简洁:
1 | |
这和 RFC 7 自己“preliminary”的定位完全一致。
它不是后来长期运行的 Host-IMP Interface 标准,而是一份早期 UCLA 实现草案。
不过它仍然非常有价值,因为它保存了:
Host 网络软件从概念转向具体操作系统实现的一个中间状态。
为什么会有这篇 RFC
一、RFC 1~6 讨论了网络,但“谁来写代码”越来越现实
RFC 1 和 RFC 2 已经提出:
- Host Software;
- Link;
- Control Link;
- TTY-like communication;
- File-like communication;
- Host-to-Host error checking。
RFC 6 又讨论:
- IMP 是否做字符转换;
- Tracing;
- RFNM;
- Host / IMP 状态;
- Format Error;
- Synchronization。
这些都是协议层面的讨论。
但真正部署时,UCLA 需要面对一台具体的 Sigma 7:
1 | |
RFC 7 就是在这个阶段出现的。
二、ARPANET 的 Host 不是一台“专用网络设备”
Host 是一台正在运行时间共享操作系统的大型计算机。
它本来已经要处理:
- 用户任务;
- 文件系统;
- 编译器;
- 终端;
- 调度;
- I/O。
网络只是新增的一类 I/O 和系统服务。
所以网络软件不能简单写成:
1 | |
它必须融入操作系统现有架构。
RFC 7 因而很自然地把网络软件拆成:
1 | |
三、多用户共享一条物理网络接口,需要 Multiplexing
RFC 7 的一个核心词是:
Multiplex(多路复用)。
同一台 Host 上同时可能有多个用户:
1 | |
它们都想通过同一个 Host-IMP Interface 发消息。
于是必须存在:
1 | |
Network Program 要把这些请求排起来,再持续填充缓冲区,让 Handler 保持工作。
反向接收时则需要:
1 | |
这就是:
1 | |
RFC 7 把它建立在 Link Identification Number 上。
四、软件层和硬件层之间需要明确边界
如果 Network Program 直接控制所有硬件细节:
1 | |
那么上层协议逻辑会和具体设备紧密耦合。
RFC 7 因此增加 Handler:
1 | |
这正是 RFC 7 最核心的架构选择。
五、这份设计并没有假装所有问题都已经解决
RFC 7 的第三章直接叫:
1 | |
作者明确承认还有几个关键接口没有搞清楚。
这符合 RFC 3 所倡导的早期 RFC 文化:
1 | |
RFC 7 本身就是这种工作方式的优秀样本。
核心内容
RFC 7 的正文可以压缩成三层结构:
1 | |
从软件架构角度,则可以整理成:
flowchart LR
U[User Programs]
NP[Network Program]
BP[Buffer Pool]
IT[Interface Table]
HP[Handler Program]
HW[Channel Hardware]
IMP[IMP]
U -->|Transmission Request| NP
NP -->|Fill| BP
NP -->|Logical Info| IT
IT --> HP
BP --> HP
HP -->|Commands / Data| HW
HW --> IMP
IMP --> HW
HW -->|Interrupt| HP
HP --> BP
HP --> IT
NP -->|Distribute Input| U
一、两个核心程序
RFC 7 的软件组织建立在两个主要程序上。
Network Program
负责:
1 | |
它更接近:
面向通信语义的软件层。
Handler Program
负责:
1 | |
它更接近:
面向具体设备和中断机制的软件层。
二、Full Duplex 让两层都要同时考虑 Input / Output
RFC 7 明确指出通信是:
1 | |
所以 Network Program 和 Handler Program 都可以逻辑上拆成:
1 | |
即:
flowchart LR
subgraph NetworkProgram[Network Program]
NO[Output / Multiplex]
NI[Input / Distribution]
end
subgraph HandlerProgram[Handler Program]
HO[Output Handler]
HI[Input Handler]
end
NO --> HO
HI --> NI
文档主要讨论输出方向。
输入方向被认为会有相似结构。
三、Network Program 的 Multiplex Function
Network Program 会把所有用户的发送请求排起来:
1 | |
并填充 Buffer Pool。
目标是让底层发送单元持续有数据可处理。
这背后已经出现了一个非常经典的生产者—消费者问题:
1 | |
而反向接收则是:
1 | |
四、Link ID 是多路复用和分发的核心标识
RFC 7 把 Link 描述为两个用户之间的逻辑连接。
Network Program 根据:
1 | |
决定:
- outgoing message 属于哪个逻辑通信;
- incoming message 应该交给哪个用户。
后续 Host-Host Protocol 会继续调整:
1 | |
这些抽象之间的关系。
所以不要把 RFC 7 的定义当成后来 NCP 的最终定义。
五、用户发数据时只需要提供三个核心参数
RFC 7 假设用户程序通过:
1 | |
或者:
1 | |
告诉 Network Program:
1 | |
即:
| 参数 | 意义 |
|---|---|
| Text Location | 用户数据在内存中的地址 |
| Text Length | 数据长度,单位 byte |
| Destination | 目标 Host |
从今天看很像:
1 | |
但 RFC 7 还不是 socket API。
它只是已经开始建立:
用户程序不必自己驱动网络硬件,而是调用操作系统提供的网络服务。
六、16-bit Host Heading
Network Program 会构造一个:
1 | |
字段如下:
| 字段 | 长度 |
|---|---|
| Trace | 1 bit |
| Spare | 2 bits |
| Link Identification Number | 8 bits |
| Destination Host | 5 bits |
| Total | 16 bits |
可以表示为:
1 | |
总计:
1 | |
这和 RFC 1 的字段宽度重新对上:
1 | |
也和 RFC 6 短暂提到的:
1 | |
形成明显反差。
这再次证明早期接口并未冻结。
七、为什么还要一个 16-bit Marking
除了 16-bit Heading,RFC 7 还在 Header 和 Text 之间插入:
1 | |
其形式可以理解为:
1 | |
也就是在正文第一位之前放一个 1,前面用 15 个 0 补齐。
目的不是业务标识,而是:
让 Text 从 Host 的 word boundary 开始。
对于今天习惯 byte stream 的开发者,这很陌生。
但 Sigma 7 这样的系统高度依赖机器字边界。
所以消息布局并不只是:
1 | |
还要兼顾:
1 | |
八、最大用户文本为什么是 1006 bytes
RFC 7 给出:
1 | |
其中:
1 | |
所以留给用户 Text:
1 | |
换算成 8-bit byte:
1 | |
因此:
1 | |
九、超过 1006 bytes 时由 Network Program 分成多个 Message
如果用户文本大于:
1 | |
Network Program 会拆成多条 Message。
例如:
flowchart LR
A[User Text<br/>2500 bytes]
B[Network Program]
M1[Message 1<br/>1006 bytes]
M2[Message 2<br/>1006 bytes]
M3[Message 3<br/>488 bytes]
A --> B
B --> M1
B --> M2
B --> M3
RFC 7 还提出:
可以考虑使用一个 Spare bit,表示多个 Message 属于同一段 Text。
这是一个非常早期的:
1 | |
思路。
但它只是 Remark,不是已经敲定的正式规范。
十、EBCDIC → ASCII Transcoding
Network Program 还要:
1 | |
这一点和 RFC 6 非常值得对照。
RFC 6 讨论的是:
1 | |
RFC 7 的具体 UCLA 软件设计却把:
1 | |
放到了:
1 | |
里。
也就是:
1 | |
这说明字符转换职责已经出现从网络设备向 Host 软件上移的趋势。
几个月后的 RFC 20 又进一步明确网络交换 ASCII 表示。
十一、Buffer Pool 是 Network Program 和 Handler 的数据交接面
Network Program 不直接把每条消息同步塞进硬件。
它先把完整 Message 放入:
1 | |
Handler 再从这些 Buffer 中取数据发送。
于是:
1 | |
两层之间被缓冲区解耦。
十二、Buffer 多大
RFC 7 计算:
1 | |
因此完整:
1 | |
Buffer 至少需要容纳:
1 | |
文档于是建议:
1 | |
作为一个方便的 Buffer Size。
也就是比最大消息略大一点:
1 | |
余量:
1 | |
这里体现出一种非常典型的工程做法:
根据协议最大单元,选择一个机器上方便管理的自然 Buffer Size。
十三、Buffer 数量会影响 Link 的利用频率
RFC 7 还特别指出:
Buffer 的数量会决定 Link 能被利用的频率。
这句话虽然没有展开算法,但非常有意义。
因为当 RFNM 或其他流控机制限制发送时,系统能否预先准备多个不同 Link 的消息,会直接影响硬件利用率。
如果只有一个 Buffer:
1 | |
如果有多个 Buffer:
1 | |
这已经是在考虑:
1 | |
之间的关系。
十四、Interface Table 是数据面之外的逻辑信息通道
RFC 7 明确区分:
1 | |
和:
1 | |
Data 通过:
1 | |
传递。
Logical Information 通过:
1 | |
传递。
所以两层之间实际上存在:
1 | |
十五、Interface Table 采用 Ring Table
RFC 7 建议:
1 | |
并使用两个指针:
1 | |
其中:
1 | |
1 | |
每个表项包含类似:
1 | |
可以抽象为:
flowchart LR
NP[Network Program]
FP[Filling Pointer]
T1[Buffer Addr + Length]
T2[Buffer Addr + Length]
T3[Buffer Addr + Length]
EP[Extracting Pointer]
HP[Handler]
NP --> FP
FP --> T1
T1 --> T2
T2 --> T3
T3 -.Ring.-> T1
EP --> HP
从现代视角看,它很像:
1 | |
或:
1 | |
但不能说今天 NIC Descriptor Ring 直接继承自 RFC 7。
更合理的理解是:
当软件的一层生产 I/O 工作、另一层异步消费时,环形描述符队列是一种非常自然的结构。
关键机制与工作流程
一、一次发送请求的完整路径
把 RFC 7 的内容串起来,一个用户发送 Text 的流程大致是:
sequenceDiagram
participant U as User Program
participant N as Network Program
participant B as Buffer Pool
participant T as Interface Table
participant H as Handler
participant HW as Channel Hardware
participant I as IMP
U->>N: text addr + length + destination
N->>N: 根据 Link 做 Multiplex
N->>N: 构造 16-bit Heading
N->>N: 插入 16-bit Marking
alt text > 1006 bytes
N->>N: 拆成多个 Message
end
N->>N: EBCDIC → ASCII
N->>B: 填充 Message
N->>T: 写 Buffer Addr + Length
N->>T: 移动 Filling Pointer
H->>T: 读取待发送描述
H->>B: 取 Message
H->>HW: 启动发送
HW->>I: Message
HW-->>H: I/O Interrupt / Device Status
H->>T: 更新 Extracting Pointer
从这个流程中能非常清晰地看到三层职责:
1 | |
二、Network Program 和 Handler 的生产者—消费者关系
可以简化成:
1 | |
其中:
1 | |
这避免 Network Program 阻塞在每一个硬件操作上。
三、为什么 Handler 要运行在 Master Mode
RFC 7 说 Handler 运行在:
1 | |
也就是需要使用特权指令。
原因非常直接。
Handler 要:
- 控制 channel hardware;
- 启动发送;
- 处理中断;
- 查看设备状态;
- 做 data chaining。
这些都属于普通用户程序不应该直接拥有的能力。
因此:
1 | |
本质上已经在强化:
1 | |
四、Output Message Processing Pipeline
RFC 7 的 Network Program 可以重构成下面的 pipeline:
flowchart TD
A[User Request]
B[Read Text Address / Length / Destination]
C[Build 16-bit Host Heading]
D[Insert 16-bit Marking]
E{Text > 1006 bytes?}
F[Split into Messages]
G[Transcode EBCDIC → ASCII]
H[Fill Buffer Pool]
I[Update Interface Table]
J[Move Filling Pointer]
K[Handler Ready]
A --> B --> C --> D --> E
E -->|Yes| F --> G
E -->|No| G
G --> H --> I --> J --> K
今天开发者会把它叫:
1 | |
这些名字更现代,但结构非常相似。
五、Input 方向为什么没有展开
RFC 7 明确说输入方向和输出方向非常相似,因此主要聚焦 output。
可以合理整理为:
1 | |
但要注意:
RFC 7 没有逐项写出完整接收算法。
因此今天重构输入流程时,只能保留:
1 | |
不能凭空补:
- checksum 细节;
- retry;
- timeout;
- exact interrupt sequence。
六、真正困难的问题出现在硬件结束边界
RFC 7 第三章提出:
Handler 怎么知道 incoming message 的真实长度?
一种选择是:
1 | |
另一种是:
1 | |
这是一个很典型的:
1 | |
问题。
数据流里只有 bit 或 byte 并不够。
接收方必须知道:
1 | |
七、Padding 与 Message End 绑定
RFC 7 还问:
1 | |
程序会告诉 MIOP/SIOP:
1 | |
最后一个 byte 发完后获得 Interrupt。
作者进一步问:
这个信号是不是也应该送给负责 padding 的特殊设备?
也就是说:
1 | |
之间还没有完全敲定。
这正是软件—硬件协同设计的难点。
八、为什么作者质疑 Host-IMP 缺少 Control Procedure
RFC 7 的第一个开放问题是:
1 | |
场景是:
1 | |
传输时如果已经出错,而 IMP 没有在本地直接解决,错误可能继续穿过网络到:
1 | |
那么就会出现:
1 | |
的问题。
于是 Deloche 问:
难道还要依赖 Host-Host Control Procedure 来处理吗?
这是非常重要的分层问题:
1 | |
九、这里已经出现“局部可靠性”和“端到端可靠性”的张力
可以画成:
flowchart LR
A[Sending HOST]
B[Local IMP]
C[Network]
D[Remote IMP]
E[Receiving HOST]
A --> B --> C --> D --> E
X[Error on HOST-IMP transmission]
X -.发生在.-> A
X -.或.-> B
Q{谁负责发现和恢复?}
Q --> L[Local HOST-IMP Control]
Q --> H[HOST-HOST Control]
RFC 7 没有给最终答案。
但这个问题非常现代:
哪一层应该修复哪一种错误?
十、User-Network Interface 还没定,因为依赖 GORDO
RFC 7 最后一个问题是:
1 | |
因为作者需要知道:
1 | |
才能真正设计:
1 | |
的接口。
后来的 RFC 11 明确说明:
1 | |
并进一步把:
- Network Program;
- Handler;
- Buffer;
- Connection;
- User Transaction;
真正映射到 GORDO 里。
所以 RFC 7 是:
1 | |
RFC 11 则更接近:
1 | |
重点概念与术语
Host-IMP Interface
Host-IMP Interface(主机—IMP 接口)。
它不是单纯一个物理插头。
在 RFC 7 中同时包括:
1 | |
是一整条 Host 接入网络的软硬件边界。
Network Program
面向用户网络请求的软件组件。
主要负责:
- Multiplex;
- Distribution;
- Message Processing;
- Link 管理相关信息;
- Header;
- Marking;
- Fragmentation;
- Transcoding;
- Buffer 管理。
Handler Program
更贴近硬件的特权程序。
负责:
- channel hardware;
- emission;
- data chaining;
- device status;
- interrupt;
- buffer consumption;
- interface table。
Multiplexing
Multiplexing(多路复用)。
多个用户共享同一网络设施。
RFC 7 根据:
1 | |
组织多路通信。
Distribution
输入方向的分发。
Network Program 根据逻辑通信标识,把收到的消息交给正确的用户。
现代通常称:
1 | |
但 RFC 7 使用 distribution。
Link
RFC 7 将其描述为两个用户之间的逻辑连接。
后续协议会继续重构 Link 与 Connection 的关系,因此只能按 RFC 7 当前语境理解。
Host Heading
Host 加在消息前面的:
1 | |
包含:
1 | |
Trace
1-bit 追踪标志。
用于网络测量和调试。
Marking
Heading 与 Text 之间的 16-bit 标记。
用于:
1 | |
并帮助定位正文起点。
Word Boundary
Word Boundary(机器字边界)。
早期大型机的软件和 I/O 高度受机器字结构影响,所以网络 Message 也需要照顾对齐。
EBCDIC
EBCDIC(Extended Binary Coded Decimal Interchange Code)。
RFC 7 的 UCLA 软件设计会把待发送的 EBCDIC 字符转成 ASCII。
ASCII
当时逐渐被采用作为网络交换字符表示。
RFC 20 后来明确提出:
1 | |
Buffer Pool
Network Program 和 Handler 之间交换 Message 数据的共享缓冲区池。
Interface Table
Network Program 和 Handler 交换逻辑描述信息的数据结构。
表项包含:
1 | |
Ring Table
环形表。
使用:
1 | |
形成生产者—消费者队列。
Filling Pointer
由 Network Program 移动。
指向:
1 | |
Extracting Pointer
由 Handler 移动。
指向:
1 | |
Data Chaining
Handler 控制通道设备时可能使用的连续数据传输机制。
原文没有展开具体硬件细节,因此不应把它强行对应到某一种现代 DMA Scatter/Gather 实现。
Master Mode
特权执行模式。
Handler 因为需要控制 I/O 和中断,所以运行在特权环境。
I/O Supervisor
操作系统中负责 I/O 管理的特权组件。
RFC 7 认为 Handler 应集成到这里。
MIOP / SIOP
RFC 7 在硬件问题中提到:
1 | |
它们属于 Sigma 7 I/O 架构相关术语。
RFC 7 本身没有为现代读者完整定义这两个缩写,因此阅读本文时只需要知道:
作者正在确认专用 Host-IMP Channel Hardware 应挂接在 Sigma 7 的哪种 I/O 处理路径上。
GORDO
UCLA Sigma 7 上的时间共享操作系统。
RFC 11 明确把 GORDO 称为:
1 | |
RFC 7 还在等待 GORDO 文档,以设计 User-Network Program Interface。
需要特别注意的点
1. RFC 7 是 Preliminary Design,不是最终规范
原文自己明确定位为初步软件设计,而且整篇以 Questions 收尾。
所以不能把其中:
- 16-bit Heading;
- 1006-byte Text;
- 1024-byte Buffer;
- EBCDIC → ASCII;
理解成后来整个 ARPANET 永久不变的规范。
2. RFC 100 已经把它标为 Obsolete
1971 年:
1 | |
因此它今天主要用于:
1 | |
而不是现行实现。
3. 原始文档是手写稿,当前文本是后期重建
碰到:
1 | |
不要自行补成一个“合理句子”。
4. RFC 7 的 Link 定义不是后来的最终 Link 模型
RFC 7:
1 | |
后来 RFC 11 又进一步区分:
1 | |
再到 RFC 33:
1 | |
抽象不断变化。
5. 16-bit Heading 很快也会变化
RFC 7 使用:
1 | |
后来的 Host-IMP 设计会使用更完整的 Leader 结构。
RFC 11 中已经出现:
1 | |
所以 RFC 7 只是接口演化中的一个阶段。
6. 8080 bits 是当时的 Host Message 上限,不是互联网永久 MTU
不要类比成:
1 | |
这些层次和定义不同。
7. 1006-byte 拆分不是 IP Fragmentation
虽然行为都是:
1 | |
但 RFC 7 的拆分发生在:
1 | |
针对用户 Text 与 Host Message 上限。
它不是后来 IP 层的 Fragmentation。
8. Spare Bit 只是建议,不能当正式“More Fragments”位
RFC 7 只是备注:
可以使用一个 Spare bit 表示多个 Message 属于同一 Text。
不能把它直接写成:
1 | |
更不能和 IPv4 的 MF 建立直接继承关系。
9. EBCDIC → ASCII 并不意味着所有 Host 都统一使用 EBCDIC
这是 UCLA 这个具体设计里的转换。
不同 ARPANET Host 本来就可能使用不同字符表示。
RFC 7 不是在说:
1 | |
10. RFC 6 与 RFC 7 的字符转换职责已经出现变化
RFC 6:
1 | |
RFC 7:
1 | |
这说明职责边界正在调整。
不要为了构造“统一架构”把两个 RFC 硬解释成完全一致。
11. RFC 6 的 5-bit Link 与 RFC 7 的 8-bit Link 冲突是真实历史现象
RFC 6:
1 | |
RFC 7:
1 | |
原始早期规范快速变化,才是正确解释。
12. 1024-byte Buffer 不是协议强制要求
RFC 7 的逻辑是:
1 | |
因此:
1 | |
很方便。
这是实现建议,不是 wire protocol 字段。
13. Ring Table 也只是一种候选实现
原文的语气是:
1 | |
这不是:
1 | |
14. Handler 不等于现代 Linux NIC Driver
二者确实有类似职责:
1 | |
但运行环境、I/O 架构、DMA 机制、协议栈边界都完全不同。
类比只能帮助理解,不应该写成技术继承关系。
15. RFC 7 明确暴露了一个尚未解决的 Error Boundary
作者质疑:
1 | |
这是文档的核心开放问题之一。
它不应该在博客中被“补答案”抹掉。
16. Input Message Length 还是开放问题
RFC 7 没有最终决定:
1 | |
所以不能根据现代经验自行补一个 Length Header。
17. GORDO 不是协议名
GORDO 是 UCLA Host 的操作系统。
RFC 7 提它,是因为:
1 | |
必须适配操作系统已有:
- process;
- program initiation;
- parameter passing;
机制。
18. 今天看到的 Fig. 1 / Fig. 2 不是原图的高保真还原
机器可读版使用 ASCII 重建图形。
博客重新用 Mermaid 表达时,应该提取:
1 | |
而不是假装恢复原始图纸的精确布局。
从今天的视角重新看这篇 RFC
RFC 7 的具体实现已经没有现实使用价值。
但从软件架构角度看,它非常“现代”。
一、它已经在做 Control Path 与 Data Path 分离
Network Program 与 Handler 之间:
1 | |
而:
1 | |
也就是:
1 | |
现代系统里非常常见:
1 | |
二、它本质上是 Producer-Consumer
RFC 7 的:
1 | |
就是一个生产者—消费者模型。
抽象后:
flowchart LR
P[Producer<br/>Network Program]
Q[Ring / Queue]
C[Consumer<br/>Handler]
P -->|Fill| Q
Q -->|Extract| C
这类模型今天存在于:
- NIC TX/RX Ring;
- Disruptor;
- Message Queue;
- IO Completion Queue;
- Kernel Driver Descriptor Queue;
- Streaming Pipeline。
三、Buffering 是解耦不同速度组件的基础
Network Program 产生 Message 的速度:
1 | |
Channel Hardware 发送速度。
所以 Buffer Pool 提供:
1 | |
如果没有 Buffer:
1 | |
有 Buffer 后:
1 | |
这就是异步 I/O 最基本的思想之一。
四、Ring 结构的价值在于固定内存和低成本循环复用
环形表天然适合:
1 | |
而不需要:
1 | |
这也是为什么类似 Ring 的结构在高性能 I/O 中至今常见。
再次强调:
这是结构上的自然相似,不是“现代 NIC Ring 起源于 RFC 7”的历史结论。
五、协议代码与设备驱动应该解耦
RFC 7 把:
1 | |
和:
1 | |
分开,很有现实意义。
如果以后 Channel Hardware 变化:
1 | |
而用户和较高层网络逻辑可以少改。
现代分层也是类似目标:
1 | |
六、用户程序应该传语义,不应该传硬件命令
RFC 7 设想用户只给:
1 | |
而不是:
1 | |
这就是一个非常关键的系统软件原则:
上层描述“我要什么”,底层决定“硬件怎样做”。
七、Fragmentation 的边界选择一直是系统设计问题
RFC 7 选择:
1 | |
把大 Text 拆成 Message。
现代系统仍然要问:
1 | |
不同层做 fragmentation,会带来不同:
- 重组成本;
- 错误恢复范围;
- 内存压力;
- 延迟;
- 重试粒度。
RFC 7 虽然很早,但已经实际面对这个问题。
八、Alignment 不是历史遗迹,高性能系统今天仍然在意
RFC 7 插入 Marking 是为了:
1 | |
今天我们依然在考虑:
1 | |
只是硬件背景变化了。
九、不要让本地接口错误无脑传播成远端错误
RFC 7 的开放问题非常值得今天微服务系统借鉴。
假设:
1 | |
如果:
1 | |
内部格式就已经出错,却一直让 B 来发现,故障定位会非常困难。
所以现代系统会尽量在最近边界进行:
1 | |
但同时又保留:
1 | |
这就是:
1 | |
的组合。
十、用户 API 设计取决于操作系统进程模型
RFC 7 无法完成 User-Network Interface,是因为还需要 GORDO 的:
1 | |
信息。
这是非常真实的系统设计约束。
API 从来不是悬空的。
它必须嵌入:
1 | |
今天设计:
- io_uring;
- async/await;
- Java NIO;
- virtual threads;
- Netty EventLoop;
也仍然受运行时模型约束。
十一、从 RFC 7 到 RFC 11,可以看到“架构设计 → 平台落地”
RFC 7:
1 | |
RFC 11:
1 | |
并详细讨论:
- allocation tables;
- buffer pages;
- Handler;
- Network;
- system calls;
- process;
- connections。
这是一个非常典型的软件工程过程:
1 | |
十二、现代“网络栈分层”不是一开始就存在的
RFC 7 最大的历史价值之一,是让我们看到这些边界正在被创造。
今天教材画:
1 | |
看起来很自然。
但 1969 年真实的问题是:
1 | |
这些争论经过多年才形成更稳定的分层。
这篇 RFC 带来的影响
RFC 7 本身没有成为长期标准。
1971 年它已经被归入:
1 | |
但它在早期 ARPANET 软件演化中有几个非常明确的位置。
一、它把 Host-IMP Interface 从“概念讨论”推进到“软件模块设计”
RFC 6 更像:
1 | |
RFC 7 则是:
1 | |
也就是从:
1 | |
进入:
1 | |
二、它是 RFC 11 的明显前身
RFC 11 的标题:
Implementation of the Host-Host Software Procedures in GORDO
其中明确写:
1 | |
并继续使用:
1 | |
这些 RFC 7 已经出现的概念。
可以把两篇关系粗略理解成:
flowchart LR
A[RFC 7<br/>Host-IMP Interface<br/>Preliminary Design]
B[RFC 11<br/>Host-Host Procedures<br/>Implementation in GORDO]
A --> B
这不是正式的 RFC Updates/Obsoletes 元数据关系,而是内容上的设计延续。
三、字符转换逐渐从 IMP 讨论转向 Host / Host-Host 约定
演化可以大致看成:
1 | |
这条线非常值得读。
它体现了网络架构逐渐从:
1 | |
转向:
1 | |
四、它提前暴露了 Host-IMP Error Handling 的分层问题
RFC 7 问:
1 | |
后续网络协议会持续围绕:
- Host-IMP protocol;
- Host-Host protocol;
- error control;
- status testing;
- RFNM;
- checksum;
进行讨论。
这说明:
错误恢复层次从 ARPANET 最早期就不是一个简单问题。
五、它保存了操作系统网络子系统成形之前的架构痕迹
RFC 7 已经出现:
1 | |
如果把这些名词全部替换成现代词汇,会显得非常熟悉。
但真正重要的是:
1969 年的人们已经意识到,网络不能只是一个应用程序,它必须成为操作系统的一项共享基础能力。
六、它也展示了“实现文档”的价值
很多 RFC 关注:
1 | |
RFC 7 却大量讨论:
1 | |
这提醒我们:
协议能不能成功,往往不只取决于规范好不好,还取决于它是否能自然嵌入真实操作系统。
七、RFC 33 后来会把 Host 侧抽象提升为 NCP
1970 年的 RFC 33 已经使用:
Network Control Program(NCP)。
它负责:
1 | |
并通过:
1 | |
为 Host 进程提供网络能力。
这比 RFC 7 的:
1 | |
抽象更成熟。
可以粗略画成:
flowchart LR
A[RFC 7<br/>Network Program + Handler]
B[RFC 11<br/>GORDO Implementation]
C[RFC 33<br/>NCP + Connection + Socket]
D[ARPANET NCP / Host-Host Protocol]
A --> B --> C --> D
这不是严格版本升级链,但很好地展示了:
1 | |
如何逐渐形成更清晰的系统抽象。
八、它最值得今天软件开发者继承的是“边界与队列”思维
RFC 7 中两个最持久的工程思想可以压缩为:
1. 明确边界
1 | |
每层都应该有清晰职责。
2. 用队列解耦
1 | |
不要让上层逻辑和硬件执行速度完全绑定。
这两点直到今天仍然存在于几乎所有高性能 I/O 系统里。
总结
RFC 7 是一篇非常“软件工程”的早期 RFC。
它不再只问:
1 | |
而开始问:
1 | |
答案是两个主要组件:
1 | |
Network Program 面向用户和网络语义:
1 | |
Handler 面向硬件:
1 | |
两者通过:
1 | |
解耦。
Interface Table 又使用一个非常经典的结构:
1 | |
一条发送请求从用户进入系统以后,大致经历:
1 | |
RFC 7 还给出了一个非常具体的 16-bit Heading:
1 | |
由于:
1 | |
所以最大 Text:
1 | |
再加上 Heading 和 Marking:
1 | |
因此实现建议使用:
1 | |
这些数字今天已经没有协议实现价值。
RFC 7 在 1971 年就被标记为:
1 | |
但它留下了几个非常值得学习的问题:
1 | |
最值得注意的是,RFC 7 没有假装这些问题已经全部解决。
它最后直接留下:
1 | |
因为当时:
- Host-IMP Control Procedure 还没定;
- Message End 的硬件信号还没定;
- Incoming Length 处理还没定;
- GORDO 用户接口也还没定。
这让 RFC 7 看起来不像今天的最终规范。
但正因为如此,它反而非常适合学习架构设计。
我们看到的不是:
1 | |
而是:
一群工程师正在把“网络通信”这个抽象,一层一层翻译成用户调用、程序模块、Ring Table、Buffer、Interrupt 和硬件命令。
从 RFC 1 到 RFC 7,ARPANET 的 Host Software 正在发生一个非常明显的变化:
1 | |
互联网并不是先有一套完美分层模型,然后大家照着实现。
真实顺序更接近:
1 | |
RFC 7,就是这个过程里非常清晰的一张架构草图。