人人都会AI编程

7.3 网络数据包完整处理流程:网卡 → 内核协议栈 → 用户进程

更新时间:2026-07-12

一个数据包从网络链路到达用户应用,需要经过多个层次的协作处理。理解这条路径,对于排查网络延迟、丢包或性能瓶颈至关重要。下面以最常见的以太网 TCP/IP 场景为例,按顺序逐步拆解。


第 1 步:网线信号 → 网卡接收

物理链路上的电信号或光信号到达网卡(NIC),网卡的 PHY 芯片将其转换为数字信号,MAC 层进行帧校验(FCS)、过滤(如检查目的 MAC 地址是否为本机或合法的广播/组播),然后将正确的数据帧通过 DMA(直接内存访问)写入内核预先分配好的环形缓冲区(ring buffer)。此时,CPU 并未介入数据搬移。

  • 关键观察点:

ethtool -g eth0 可查看 ring buffer 大小。
ip -s link show eth0 可看到丢包计数(RX: errors, dropped)。


第 2 步:硬中断通知 CPU

DMA 完成后,网卡会发送一个硬件中断,通知 CPU 有数据到达。中断处理函数被调用,它的工作非常简短:屏蔽该网卡的中断,并向 ksoftirqd 内核线程调度一个软中断(NET_RX_SOFTIRQ),然后快速返回。这种“硬中断触发软中断”的做法,是为了避免长时间占用中断上下文导致系统响应变慢。

  • 关键观察点:

cat /proc/interrupts | grep eth0 可以看到网卡中断计数和亲和性 CPU。
中断均衡可以通过 irqbalance 或手动设置 /proc/irq/<IRQ>/smp_affinity 调整。


第 3 步:软中断与 NAPI 轮询

软中断处理函数 net_rx_action() 会调用网卡驱动注册的 NAPI 轮询函数(如 igb_pollixgbe_poll),从环形缓冲区中取出数据包(sk_buff 结构),交给内核协议栈。NAPI 机制在流量较大时会持续轮询(budget 控制每次轮询取包量),避免中断风暴;流量较小时则切换回中断模式,节省 CPU。

  • 关键观察点:

cat /proc/net/softnet_stat 显示各 CPU 的软中断处理统计,第三列是 net_rx_action 处理数据包时达到预算上限的次数,若持续增长可能需要调大 net.core.netdev_budget


第 4 步:数据链路层处理

数据包从驱动传入 netif_receive_skb(),进入协议栈抽象层。内核会根据数据包的 EtherType 字段,判断上层协议(如 IPv4、IPv6、ARP、VLAN 等),并调用对应的处理函数。如果是桥接或 bonding 设备,还会进行相应处理。此阶段还会进行 netfilter PREROUTING 链的钩子处理(如 iptables 的 raw/mangle 表)。


第 5 步:网络层(IP 层)处理

IPv4 数据包进入 ip_rcv() 函数,进行基本的合法性检查(校验和、版本、头长度、TTL 等)。通过检查后,进入 netfilter PREROUTING 链,此时可以执行 DNAT(目标地址转换)等操作。随后,路由子系统通过 ip_route_input() 查找目的地址的路径:是交给本机上层处理,还是转发到其他网络设备?

若目标为本机,数据包进入 ip_local_deliver()。此时会根据需要进行 IP 分片重组,然后再次穿过 netfilter INPUT 链(filter 表在此可以设置包过滤),最终调用传输层协议(如 TCP、UDP)的接收函数。

  • 关键观察点:

netstat -s(或 nstat -a)可查看 IP 层统计,包括校验和错误、分片失败等。
iptables -t raw -nvL PREROUTING 查看原始表规则命中情况。


第 6 步:传输层(TCP/UDP)处理

以 TCP 为例,数据包到达 tcp_v4_rcv()。内核首先根据五元组(源 IP、目的 IP、源端口、目的端口、协议)查找对应的 socket。如果找不到,直接回复 RST 复位包(若目的端口未监听)或交由 blackhole 处理。

找到 socket 后,数据包进入 TCP 协议处理逻辑:检查序列号、窗口大小、处理重传、乱序重组等。合法的数据会被放置在 socket 的接收缓冲区(receive buffer)中,等待用户进程读取。此时 socket 状态会更新,并通过 epoll/select 等机制通知或唤醒阻塞的进程。

  • 关键观察点:

ss -tlnpss -tnp 查看 TCP socket 状态与队列积压。
ss -im 可显示详细的内存使用和收发队列信息。
cat /proc/net/snmp 给出 TCP 段统计。


第 7 步:用户进程读取数据

用户进程调用 read()recv()recvfrom() 等系统调用时,陷入内核,内核从 socket 接收缓冲区中拷贝数据到用户空间的缓冲区,并返回实际拷贝的字节数。如果缓冲区为空且 socket 为非阻塞,则返回 EAGAIN;若为阻塞模式,进程挂起睡眠,直到数据到达并被唤醒。

从网卡 DMA 到用户进程拿到数据,中间的缓冲区拷贝、软中断处理、协议栈遍历都可能在低延迟场景中成为瓶颈。常见优化包括:使用零拷贝(sendfilesplice)、加大缓冲区、开启网卡多队列与 RSS(Receive Side Scaling),以及使用 BPF(eBPF)在数据路径早期过滤或分流数据。


整体流程速览

网线信号 → 网卡(PHY/MAC) → DMA 写入 ring buffer
   → 硬中断 → 软中断 → NAPI 轮询
   → 链路层处理(eth_type_trans)
   → netfilter PREROUTING
   → IP 层(ip_rcv → 路由 → ip_local_deliver)
   → netfilter INPUT
   → TCP/UDP(tcp_v4_rcv → socket 接收队列)
   → 唤醒/epoll 通知
   → 用户进程 read()/recv()

实用排查思路

  • 网卡丢包:检查 ethtoolip link、ring buffer 大小。
  • 软中断瓶颈:查看 /proc/interruptssoftnet_stat,必要时调整中断亲和性或使用 RPS/RFS。
  • 路由/防火墙问题:用 iptables -nvL 查看规则命中和丢包计数。
  • 应用接收慢:用 ss 检查 Recv-Q 是否有积压,判断是 CPU 处理不过来还是用户程序未及时读取。

掌握这条路径,你就能够根据现象(如丢包、重传、时延增加)快速定位到是硬件、内核驱动、协议栈配置还是用户程序的环节出了问题,从而采取针对性的调整措施。