TCP/IP详解 卷2:实现
书籍概述
本书是W. Richard Stevens的经典之作,深入剖析了4.4BSD-Lite(简称Net/3)操作系统中TCP/IP协议栈的完整实现。全书包含约15,000行源代码,是理解网络协议内部工作原理的权威参考。
与卷1侧重协议规范不同,卷2从实现者视角出发,展示了协议代码如何组织、数据如何流动、系统调用如何到达内核。
核心架构:三层网络代码组织
Net/3内核联网代码分为三层(对应OSI模型):
-
插口层(Socket Layer) — 协议无关接口
- 所有系统调用(socket、bind、connect、sendto等)的入口
- 验证参数后调用协议相关代码
- 对应OSI第5层(会话层)
-
协议层(Protocol Layer) — 四种协议族
- TCP/IP(互联网协议族,本书重点)
- XNS(Xerox网络系统,已淘汰)
- OSI协议
- Unix域协议(本地IPC)
- 对应OSI第3-4层(网络/运输层)
-
接口层(Interface Layer) — 设备驱动
- 以太网、SLIP、PPP、环回等
- 对应OSI第1-2层(物理/数据链路层)
关键数据结构
描述符与socket结构链
进程调用socket()后,内核建立以下数据结构链:
进程表项 proc{}
→ p_fd → filedesc{}
→ fd_ofiles[] → file{}(DTYPE_SOCKET类型)
→ f_data → socket{}
→ so_pcb → inpcb{}(Internet协议控制块)
- inpcb 包含本地/远端IP地址(inp_laddr/inp_faddr)和端口号(inp_lport/inp_fport)
- 所有UDP PCB通过 inp_next/inp_prev 组成双向循环链表,表头为全局变量 udb
- 数据到达时通过端口号(可能还需IP地址)反向查找对应的socket
mbuf:存储器缓存
mbuf是BSD网络代码的基本存储单元,所有网络数据都通过mbuf链传递。
- 标准mbuf大小:128字节(20字节首部 + 108字节数据空间)
- 带分组首部的mbuf:首部额外占用8字节,数据空间100字节
- mbuf通过 m_next 指针链接成链表
- 大数据使用簇(cluster):1024或2048字节的外部缓存,通过引用计数共享
- m_flags:M_PKTHDR(分组首部)、M_EXT(外部簇)
- m_type:MT_SONAME(插口地址)、MT_DATA(数据)等
数据发送时mbuf链的演变(UDP 150字节数据示例):
- 目标地址存入独立mbuf(MT_SONAME,16字节sockaddr_in)
- 用户数据分两个mbuf存储(100+50字节)
- 新增mbuf放IP+UDP首部(28字节),首部前移留出72字节
- 以太网层再添加14字节以太网帧首部
UDP数据报的完整旅程
输出路径(进程→网络)
进程 sendto()
→ 插口层:复制地址和数据到mbuf链
→ UDP输出:添加UDP首部(8字节),计算检验和
→ IP输出:填写IP首部(20字节),选路,必要时分片
→ 以太网输出:ARP解析MAC地址,添加14字节帧首部
→ 设备驱动:放入输出队列,复制到传输缓存,发送
关键细节:
- 150字节数据在内核中被遍历3次(复制到mbuf、UDP检验和、复制到设备缓存)
- IP检验和由IP层计算,UDP检验和是可选的(IPv4)
- 数据到达驱动前不排队,高层只添加首部后立即传递
输入路径(网络→进程)
以太网硬件中断
→ 驱动程序:数据读入mbuf,提交通用以太网输入例程
→ 识别帧类型为IP,加入IP输入队列,触发软中断
→ IP输入(异步软中断):验证检验和、处理选项、转发或上交
→ UDP输入:验证UDP首部,遍历udb链表匹配PCB
→ 数据加入socket接收队列,唤醒进程
→ recvfrom():数据从mbuf复制到用户缓存
关键区别:输入是异步的(硬件中断驱动),输出是同步的(系统调用驱动)。
TCP核心机制
连接状态机
TCP通过11种状态管理连接生命周期,关键转换:
- 三次握手:SYN_SENT → ESTABLISHED(客户端);LISTEN → SYN_RCVD → ESTABLISHED(服务端)
- 连接关闭:FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT(2MSL等待)
- RST用于异常终止和拒绝无效报文段
定时器体系
Net/3为每条TCP连接维护多个定时器:
- 重传定时器:未收到ACK时重发数据
- 持续定时器:窗口大小为0时定期探测
- 保活定时器:检测空闲连接是否存活
- 2MSL定时器:TIME_WAIT状态等待
- 延迟ACK定时器:累积确认减少ACK流量
流量控制与拥塞控制
- 滑动窗口:接收方通过通告窗口(rcv_wnd)控制发送速率
- 慢启动:连接开始时拥塞窗口(cwnd)指数增长
- 拥塞避免:到达慢启动门限(ssthresh)后线性增长
- 快速重传:收到3个重复ACK立即重传,不等待超时
- 快速恢复:重传后不进入慢启动,而是减半窗口继续
时间戳选项(RFC 1323)
- 用于精确测量RTT(往返时间),每个报文段可获得独立样本
- 时间戳时钟频率建议:1b/ms ~ 1b/s(Net/3采用2b/s)
- 频率过高(1b/ms):32位时间戳24.8天回绕
- 频率过低(500ms/bit):34年回绕
- PAWS(防止序号回绕):利用时间戳丢弃重复的旧报文段
IP层关键实现
路由表查找
采用**基数树(radix tree)**结构,支持变长掩码:
- 主机路由(掩码全1)优先于网络路由
- 默认路由作为最后手段
- PCB中缓存路由指针(ro_rt),避免每次发送都查询路由表
- ICMP重定向可动态创建(RTF_DYNAMIC)或修改(RTF_MODIFIED)路由
IP分片与重装
- 输出时:超过出接口MTU的数据报被分片,每片独立路由
- 输入时:根据(源IP,目的IP,协议,ID)四元组组装
- 只有偏移量为0的第一个分片携带完整IP选项
- 重装超时后丢弃所有分片,只对第一个分片发送ICMP超时报文
广播与多播
- 有限广播地址:255.255.255.255(不经过路由器转发)
- 以太网多播映射:IP多播组224.0.0.1 → MAC 01:00:5e:00:00:01
- IGMP管理组成员关系,报告延迟随机化以减少报文数量
- 多播路由通过mrouted守护进程和隧道接口实现
ARP实现
- 以太网输出时通过ARP将32位IP地址解析为48位MAC地址
- ARP缓存条目正常超时20分钟
- 不完整条目(已发送请求未收到应答)0~5分钟超时
- 收到ARP请求时自动学习发送方的IP-MAC映射
- 环回接口不需要ARP
ICMP协议要点
- ICMP差错报文不对广播/多播数据报生成(避免广播风暴)
- 不对ICMP差错报文再生成差错(防止无限循环)
- 只对分片数据报的第一个分片生成差错
- ICMP重定向只由路由器生成,主机据此更新路由表
- 回显请求/应答(ping)是最常用的诊断工具
原始IP与BPF
原始IP插口(Raw Socket)
三种用途:
- 收发ICMP/IGMP报文(如ping)
- IP_HDRINCL选项自行构造IP首部
- 支持内核不处理的其他IP协议
接收过滤条件(可组合):协议字段、源IP(connect指定)、目的IP(bind指定)。
BPF(BSD分组过滤器)
- 工作在数据链路层,比原始IP更底层
- 可接收指定接口的所有数据帧(包括非IP协议)
- 支持混杂模式,可抓取发往其他主机的数据帧
- 通过内核态过滤器减少数据复制
- 输出需自行构造完整数据链路层首部,不经过IP选路
插口层编程要点
关键系统调用
- socket():创建插口,分配file{}、socket{}、inpcb{}
- bind():绑定本地IP和端口(服务器通常绑定通配地址+熟知端口)
- connect():TCP建立连接;UDP仅记录目的地址(可调用两次以”断开”)
- listen():标记插口为被动接收,设置连接队列长度
- accept():从完成队列取出连接,返回新的描述符
- sendto()/recvfrom():无连接通信(UDP典型)
- send()/recv():面向连接通信(TCP典型)
插口缓冲区
- 每个socket有发送和接收两个缓冲区
- 高水位标记(sb_hiwat)限制总字节数
- 缓冲区满时进程阻塞(默认)或返回错误(非阻塞模式)
- select()可同时监控多个描述符的可读性/可写性/异常
性能与设计启示
- mbuf设计权衡:小内存块减少碎片但增加遍历开销,簇提高大块数据效率但需引用计数管理
- 异步输入处理:硬件中断仅做最少工作,协议处理延迟到软中断,避免阻塞其他中断
- 路由缓存:PCB中保存路由指针避免重复查找,但路由变更时需失效处理
- 首部预留:mbuf在数据前后预留空间,各层添加首部时无需复制已有数据
- 检验和开销:UDP检验和需要遍历全部数据,是主要性能瓶颈之一
- 中断优先级:splimp(网络硬件中断)> splnet(网络软中断)> 进程级,保护共享数据结构
与卷1的关联阅读
本书应配合TCP-IP详解卷1-协议阅读:
- 卷1说明协议”应该怎样”(规范、报文格式、状态机)
- 卷2说明代码”实际怎样”(数据结构、函数调用、边界处理、实现bug)
- 卷2中反复指出Net/3代码与RFC规范的偏差及实际兼容性考量
延伸阅读
- TCP-IP详解卷3-TTCP事务协议HTTPNNTP和UNIX域协议
- 4.4BSD-Lite源代码(附录B提供获取方式)
- W. Richard Stevens《Unix网络编程》(插口API编程细节)