1. 网络基础回顾:从分层模型到Linux协议栈

做网络协议栈开发这些年,我见过不少同事一上来就扎进代码,结果被各种术语绕晕。其实,理解网络通信,关键就两件事:分层和封装。今天咱们先把地基打牢。

1.1 OSI七层模型 vs TCP/IP四层模型

你肯定听过OSI七层模型。理论上是完美的,但现实中没人完全照着它实现。为什么?太复杂了。七层划分过于细致,商用成本高。真正统治互联网的是TCP/IP四层模型。

我习惯把两者放在一起对比,这样能看清TCP/IP做了哪些“裁剪”。

OSI七层模型 TCP/IP四层模型 核心职责 典型协议/设备
应用层 应用层 为用户提供网络服务接口 HTTP, FTP, DNS, SSH
表示层 数据格式转换、加密、压缩 SSL/TLS, JPEG, ASCII
会话层 建立、管理、终止会话 NetBIOS, RPC
传输层 传输层 端到端可靠传输、流量控制 TCP, UDP
网络层 网络层 路由选择、逻辑寻址 IP, ICMP, ARP
数据链路层 网络接口层 帧封装、介质访问控制、差错检测 以太网, MAC地址, VLAN
物理层 比特流传输、电气特性 网线, 光纤, 集线器

核心观点:TCP/IP模型把OSI上三层合并为应用层,把下两层合并为网络接口层。这种“务实”的设计,让协议栈实现更轻量,也更容易在操作系统内核中落地。

1.2 数据封装与解封装:数据包的“俄罗斯套娃”

网络通信的本质,就是数据在每一层被“套上”一个头部,然后在接收端一层层“拆开”。这个过程,我称之为“俄罗斯套娃”。

举个例子,你在浏览器里输入一个URL,按下回车。数据从应用层一路向下,每经过一层,都会加上该层的头部信息。

发送端:封装过程

  1. 应用层:生成HTTP请求报文(比如GET /index.html)。
  2. 传输层:加上TCP头部(源端口、目的端口、序列号等),形成TCP段。
  3. 网络层:加上IP头部(源IP、目的IP、TTL等),形成IP数据报。
  4. 网络接口层:加上以太网帧头部(源MAC、目的MAC、类型),尾部加上FCS校验,形成以太网帧。

接收端:解封装过程

  1. 网络接口层:检查MAC地址,校验FCS,去掉帧头部和尾部,交给网络层。
  2. 网络层:检查IP地址,去掉IP头部,交给传输层。
  3. 传输层:检查端口号,去掉TCP头部,重组数据,交给应用层。
  4. 应用层:解析HTTP报文,交给浏览器渲染。

避坑指南:我曾经在调试一个网络性能问题时,发现数据包总是丢。查了半天,结果是网卡驱动在解封装时,FCS校验没通过就直接丢弃了。所以,数据链路层的校验不是摆设,很多“玄学”问题都出在这一层。

1.3 Linux网络协议栈概览:内核里的“高速公路”

好了,理论讲完,咱们看看Linux内核里到底是怎么实现这套模型的。Linux网络协议栈,说白了就是内核里一套处理网络数据包的软件系统。它不是一个单一的模块,而是一系列模块的集合。

我个人习惯把协议栈分成三个层次来看:

  • 套接字层(Socket Layer):给用户空间的应用程序提供系统调用接口(如socket, bind, send, recv)。它是用户态和内核态的“桥梁”。
  • 传输层协议模块:实现TCP、UDP等协议。这是协议栈最复杂的部分,涉及拥塞控制、重传、滑动窗口等。
  • 网络层与网络接口层:实现IP、ICMP、ARP,以及和网卡驱动交互的部分。数据包在这里被路由、转发、最终发送到物理介质。

你想想看,一个数据包从应用程序发出,到网线,中间要经过多少函数调用?几十个。每个函数都可能成为性能瓶颈。

注意:Linux协议栈是软中断驱动的。网卡收到数据后,通过硬中断通知CPU,CPU在软中断上下文里处理数据包。如果软中断处理时间过长,会导致网络延迟飙升。这是调优的关键点。

1.4 知识体系总览:一张图看懂协议栈

下面这张图,是我画给团队新人的。它把上面讲的所有内容串在了一起。你盯着看三分钟,就能建立起全局观。

Linux网络协议栈全景与数据封装流程 Linux网络协议栈全景与数据封装流程 应用层 HTTP / DNS / SSH 传输层 TCP / UDP 网络层 IP / ICMP / ARP 网络接口层 以太网 / 网卡驱动 数据封装过程 HTTP请求报文 TCP头 HTTP请求报文 IP头 TCP段 帧头 IP数据报 FCS Linux内核实现 Socket层 sys_sendmsg / sys_recvmsg TCP/UDP模块 tcp_v4_rcv / udp_rcv IP层 ip_rcv / ip_output 网络设备层 dev_queue_xmit / netif_rx 数据从应用层向下,每层加头;接收端从下向上,每层去头。 Linux内核中的协议栈模块与OSI/TCP/IP层次一一对应。

1.5 为什么要在内核里实现协议栈?

你可能想问:为什么不在用户态实现?嗯,这个问题我当年也纠结过。后来在项目中踩了坑才明白:

  • 性能:内核态可以直接操作硬件(网卡),避免频繁的用户态/内核态切换。每次切换都有开销,高并发场景下不可接受。
  • 安全性:协议栈是共享资源,多个进程同时使用。内核统一管理,防止进程间互相干扰或恶意篡改数据。
  • 统一性:所有网络应用都通过相同的系统调用接口访问协议栈,行为可预测,调试更方便。

个人经验:我曾经参与过一个DPDK项目,把协议栈搬到用户态。性能确实提升了30%,但代价是应用逻辑变得极其复杂,而且失去了内核的安全隔离。所以,除非你有极致的性能需求,否则别轻易动内核协议栈。

1.6 本章小结

这一章,我们干了三件事:

  • 对比了OSI和TCP/IP模型,明白了TCP/IP为什么是“务实”的选择。
  • 拆解了数据封装和解封装过程,理解了数据包在每一层的“变身”。
  • 鸟瞰了Linux网络协议栈的整体架构,知道了它和理论模型的对应关系。