本页目录

网络协议栈概述

覆盖: sk_buff → NAPI → softirq (NET_RX/NET_TX) → 协议栈分层 → socket API → net_device → multi-queue 内核版本: 2.6 ~ 6.x

概述

Linux 网络栈是世界上使用最广泛的 TCP/IP 实现。从网卡 DMA 收包到用户态 read() 返回,数据包经过 10+ 层处理。本文追踪这个完整路径,聚焦核心数据结构和设计决策。

sk_buff: 数据包描述符

// include/linux/skbuff.h
struct sk_buff {
    struct sk_buff      *next, *prev;  // 双向链表 (调度队列)
    struct sock         *sk;           // 所属 socket

    ktime_t             tstamp;        // 时间戳 (SO_TIMESTAMPING)
    struct net_device   *dev;          // 输入/输出设备

    // 数据指针 (分段的线性 + 非线性的 frags)
    unsigned char       *head, *data;
    unsigned int        len, data_len;
    sk_buff_data_t      tail, end;

    // 协议头指针 (逐层解析后设置)
    __be16              protocol;      // L3 (ETH_P_IP, ETH_P_IPV6)
    unsigned char       *transport_header;  // L4 (TCP/UDP)
    unsigned char       *network_header;    // L3 (IP header)
    unsigned char       *mac_header;        // L2 (Ethernet)

    // 分段/重组
    unsigned int        gso_size, gso_segs;  // GSO: 大包 → 硬件分段
    unsigned short      nr_frags;            // 非连续页面数
};

sk_buff 的核心设计是 head/data/tail/end 四指针布局:数据在 head~end 分配的缓冲区中,data~tail 是有效数据,head~datatail~end 是 headroom/tailroom。协议栈每加一层协议头,data 指针往前移,复用已有的 headroom——无需拷贝。

NAPI: 中断 + 轮询混合

// net/core/dev.c
// 问题: 高速网卡 (10Gbps+) 每秒百万包 → 每个包一次中断 → 中断风暴
// NAPI: 第一个包用中断通知 → 关中断 → softirq 中轮询收包

// 网卡驱动:
//   1. 收到包 → 关 RX 中断 → napi_schedule(&napi)
//   2. NET_RX_SOFTIRQ 触发 → napi_poll()
//      → 批量收包 (一个 budget, 通常 300 包)
//      → 处理完一批 → 如果还有包 → 继续轮询
//      → 如果包耗尽 → 开 RX 中断 → 回到步骤 1

收包完整路径

flowchart TD
    DMA["网卡 DMA<br/>→ 环形缓冲区 (RX ring)"]
    DMA -->|"硬件中断"| IRQ["驱动中断 handler<br/>napi_schedule()"]
    IRQ -->|"NET_RX_SOFTIRQ"| POLL["napi_poll()<br/>批量收包, 最多 300 包"]

    POLL -->|"每包"| NETIF["__netif_receive_skb()"]

    NETIF --> XDP["XDP hook (bpf)<br/>最早处理点"]

    NETIF --> RXH{"RX handler<br/>列表检查"}
    RXH -->|"tcpdump"| AF_PACKET["AF_PACKET<br/>拷贝到 packet socket"]
    RXH -->|"bridge/bonding/VLAN"| REDIR["可能重定向"]

    NETIF --> DELIVER["deliver_skb()"]

    DELIVER --> IP_RCV["ip_rcv()<br/>L3 (IPv4)"]

    IP_RCV --> IP_LOCAL["ip_local_deliver()"]

    IP_LOCAL --> NF{"netfilter<br/>PREROUTING"}
    NF -->|"FORWARD"| FWD["转发到其他接口"]
    NF -->|"INPUT"| IP_FINISH["ip_local_deliver_finish()"]

    IP_FINISH --> TCP_RCV["tcp_v4_rcv()<br/>L4 (TCP)"]

    TCP_RCV --> TCP_DO["tcp_v4_do_rcv()"]

    TCP_DO --> STATE{"连接状态?"}
    STATE -->|"ESTABLISHED"| FAST["tcp_rcv_established()<br/>fast path ⚡"]
    STATE -->|"LISTEN"| SLOW["tcp_rcv_state_process()"]
    SLOW --> QUEUE["数据入 receive queue"]

    FAST --> QUEUE
    QUEUE --> WAKE["sk_data_ready()<br/>唤醒阻塞的 read() ✅"]

    classDef hw fill:#fff3e0,stroke:#ef6c00
    classDef irq fill:#ffebee,stroke:#c62828
    classDef l2 fill:#e3f2fd,stroke:#1565c0
    classDef l3 fill:#f3e5f5,stroke:#7b1fa2
    classDef l4 fill:#e8f5e9,stroke:#2e7d32
    classDef decision fill:#fff8e1,stroke:#f9a825
    class DMA,IRQ hw
    class POLL,NETIF,XDP,RXH,AF_PACKET,REDIR l2
    class IP_RCV,IP_LOCAL,IP_FINISH,DELIVER,NF,FWD l3
    class TCP_RCV,TCP_DO,FAST,SLOW,QUEUE,WAKE l4
    class STATE decision

Multi-Queue 与 RSS

RSS (Receive Side Scaling):
  网卡用 hash(src_ip, dst_ip, src_port, dst_port) % N
  → N 个接收队列 → N 个 CPU

  NAPI 的 napi_struct 是 per-queue 的:
    → 每个队列独立 NAPI → 收包并行
    → 相同 flow 的包始终去同一队列 → TCP 保序

XPS (Transmit Packet Steering):
  发送端的多队列: 类似但用于 TX

net_device: 网络设备抽象

// include/linux/netdevice.h
struct net_device {
    char                name[IFNAMSIZ];    // eth0, wlan0
    const struct net_device_ops *netdev_ops;
    // → ndo_open, ndo_stop, ndo_start_xmit, ndo_set_rx_mode, ...

    struct net_device_stats stats;         // 驱动填充的统计
    struct list_head    qdisc_list;        // 排队规则 (tc)

    unsigned int        num_rx_queues;
    unsigned int        num_tx_queues;
    struct netdev_rx_queue  *_rx;          // RX 队列数组 (per-queue NAPI)
};

调试

# 收发包统计
cat /proc/net/dev
ip -s link

# 软中断统计 (NET_RX/NET_TX)
cat /proc/softirqs | grep NET

# NAPI 统计 (per-queue 轮询)
ethtool -S eth0 | grep -E 'rx_packets|rx_bytes|tx_'

# 丢包原因 (drop counter)
dropwatch -l kas

参考

  • 源码⁠: net/core/dev.c, include/linux/skbuff.h, include/linux/netdevice.h
  • 内核文档⁠: Documentation/networking/
  • LWN: "The Linux networking stack", "NAPI design"

关键词: sk_buff, NAPI, NET_RX_SOFTIRQ, multi-queue, RSS, net_device, XDP