2. 网络协议栈基础回顾:Linux内核网络协议栈分层模型、数据包收发路径
在深入探讨TCP/IP卸载技术之前,必须牢固掌握Linux内核网络协议栈的基础架构。本章将回顾其经典的分层模型,并详细追踪一个数据包从网卡到用户态应用(以及反向)的完整路径。理解这些基础是评估卸载功能收益与代价的前提。
2.1 Linux内核网络协议栈分层模型
Linux内核网络协议栈遵循经典的TCP/IP分层模型,但在实现上进行了内核态的优化与整合。从下至上,主要分为以下四层:
| 层次 | 内核组件/模块 | 核心职责 | 关键数据结构/函数 |
|---|---|---|---|
| 网络设备层 | 网卡驱动、NAPI | 与物理硬件交互,收发原始帧;处理中断与轮询 | struct net_device, struct sk_buff, netif_receive_skb() |
| 网络层 | IPv4/IPv6、路由、Netfilter | 寻址、路由选择、分片与重组、防火墙过滤 | struct iphdr, ip_rcv(), ip_route_input(), NF_HOOK() |
| 传输层 | TCP、UDP | 端到端连接管理、流量控制、可靠传输(TCP)、数据报传输(UDP) | struct tcphdr, tcp_v4_rcv(), udp_rcv(), struct sock |
| 套接字层 | BSD Socket、INET Socket | 提供系统调用接口(如 read, write, sendmsg),抽象传输层细节 |
struct socket, struct sock, sock_read(), sock_sendmsg() |
关键数据结构:
struct sk_buff —— 这是贯穿整个协议栈的核心“信使”。它承载着数据包的所有信息,包括协议头指针、数据载荷、设备信息、时间戳等。理解 sk_buff 的头部预留(headroom)和数据指针操作,是理解零拷贝和卸载技术的基础。
下图展示了数据包从网卡驱动到套接字层所经过的主要内核模块与钩子点:
2.2 数据包接收路径详解
当一个数据帧到达网卡时,它经历了一系列精心设计的步骤,从硬件中断到内核协议栈处理,最终被用户态进程读取。以下是标准收包路径的详细步骤:
- DMA写入:网卡通过DMA(直接内存访问)将数据帧直接写入内核预分配的内存缓冲区(Ring Buffer),无需CPU干预。
- 硬中断:DMA完成后,网卡触发硬件中断,通知CPU有新的数据包到达。
- NAPI软中断:中断处理程序(通常是NAPI机制)关闭该网卡的中断,并将其加入轮询列表,然后触发软中断(
NET_RX_SOFTIRQ)。 - 驱动层处理:软中断处理函数调用驱动的NAPI轮询方法(
poll()),从Ring Buffer中获取数据帧,并将其封装为struct sk_buff。 - 设备层入口:驱动调用
netif_receive_skb()将sk_buff送入内核协议栈核心。这里会经过流量控制(如RPS/RFS)和桥接处理。 - 网络层处理:根据以太网类型字段,协议栈将数据包分发到对应的网络层处理函数(如
ip_rcv())。在此进行路由查找、IP选项处理、Netfilter钩子检查。 - 传输层处理:网络层处理完毕后,根据IP头中的协议字段,将数据包传递给传输层(如
tcp_v4_rcv()或udp_rcv())。TCP层会进行连接查找、序列号检查、窗口管理。 - 套接字入队:传输层将数据放入对应套接字的接收缓冲区(Receive Buffer)。
- 用户态读取:用户态进程通过系统调用(如
read()或recvmsg())将数据从内核缓冲区拷贝到用户空间缓冲区。
性能关键点: 上述路径中,数据拷贝(DMA到内核、内核到用户)和上下文切换(硬中断、软中断、系统调用)是主要的性能开销来源。TCP/IP卸载技术(如TSO、GRO、RFS)正是为了减少这些环节的CPU负载而设计的。
下图以流程图形式直观展示了收包路径中的关键函数调用链:
2.3 数据包发送路径详解
发送路径是接收路径的逆过程,但同样包含多个关键步骤和潜在的瓶颈:
- 系统调用:用户态进程调用
send()或sendmsg(),陷入内核。 - 套接字层:内核将用户数据从用户空间拷贝到内核空间的套接字发送缓冲区。
- 传输层封装:TCP/UDP层添加协议头。TCP可能进行分段(如果未启用TSO),计算校验和。
- 网络层封装:IP层添加IP头,执行路由查找,确定下一跳和输出网卡。可能进行分片。
- Netfilter钩子:在
NF_INET_LOCAL_OUT和NF_INET_POST_ROUTING点进行过滤和NAT。 - 设备层排队:数据包被送入输出网卡的排队规则(qdisc)。默认是
pfifo_fast,也可能是fq_codel或htb等。 - 驱动发送:qdisc出队后,调用驱动的发送函数(
ndo_start_xmit),将数据帧放入网卡的发送Ring Buffer。 - DMA发送:网卡通过DMA从Ring Buffer读取数据,并将其发送到物理链路上。发送完成后触发完成中断。
常见性能陷阱: 发送路径中的 qdisc锁竞争 和 TCP小包(Nagle算法) 是导致CPU使用率高和吞吐量上不去的主要原因。TSO/GSO卸载技术通过让网卡硬件进行大包分段,显著减少了CPU在传输层的处理负担。
2.4 关键性能指标与瓶颈分析
理解协议栈路径后,我们可以识别出影响网络性能的核心指标和常见瓶颈:
| 指标 | 描述 | 常见瓶颈 |
|---|---|---|
| 吞吐量 (Throughput) | 单位时间内成功传输的数据量(Gbps) | CPU单核瓶颈、PCIe带宽限制、内存带宽 |
| 每秒数据包数 (PPS) | 单位时间内处理的数据包数量 | 中断处理开销、锁竞争(如qdisc锁)、协议栈遍历开销 |
| 延迟 (Latency) | 数据包从发送到接收确认的往返时间(RTT) | 中断合并(Interrupt Coalescing)、NAPI轮询延迟、qdisc排队 |
| CPU使用率 | 处理网络数据所消耗的CPU百分比 | 数据拷贝、校验和计算、上下文切换 |