Linux内核TCP网络模块深度分析

Linux内核TCP网络模块深度分析


目录

  1. 概述
  2. 整体架构
  3. 初始化与注册机制
  4. 网卡驱动层分析
  5. 网络协议栈层分析
  6. Socket层与系统调用
  7. 模块层次关系总结
  8. 关键数据结构速查
  9. 完整调用链
  10. 性能优化技术总结
  11. 常见问题诊断
  12. 调试技巧
  13. 参考资料

1. 概述

本文档详细分析Linux内核3.10.0中TCP报文的接收和读取流程,涵盖两个核心维度:

1.1 分析维度

维度一:报文接收路径(网卡→内核)

复制代码
硬件网卡 → 硬件中断 → NAPI软中断 → 协议栈处理 → Socket接收队列

维度二:应用读取路径(内核→用户空间)

复制代码
系统调用 → VFS层 → Socket层 → TCP协议层 → 数据拷贝 → 用户缓冲区

1.2 核心模块层次

复制代码
+------------------+
|   应用层进程     |  (read/recv系统调用)
+------------------+
         ↓
+------------------+
|   系统调用层     |  (sys_read, sys_recvfrom)
+------------------+
         ↓
+------------------+
|    VFS层         |  (vfs_read, sock_aio_read)
+------------------+
         ↓
+------------------+
|   Socket层       |  (socket, sock结构体)
+------------------+
         ↓
+------------------+
|   TCP协议层      |  (tcp_recvmsg, tcp_data_queue)
+------------------+
         ↓
+------------------+
|   IP协议层       |  (ip_rcv, ip_local_deliver)
+------------------+
         ↓
+------------------+
|   链路层         |  (eth_type_trans, __netif_receive_skb)
+------------------+
         ↓
+------------------+
|   NAPI软中断     |  (net_rx_action, napi_poll)
+------------------+
         ↓
+------------------+
|   网卡驱动       |  (驱动poll函数, DMA)
+------------------+
         ↓
+------------------+
|   硬件网卡       |  (物理层接收)
+------------------+

2. 整体架构

2.1 数据流向总览

复制代码
[报文接收流程 - 自底向上]

┌─────────────────────────────────────────────────────────────┐
│  Layer 0: 物理硬件层                                        │
│  ┌──────────┐                                               │
│  │ NIC硬件  │ → DMA写入内存 → 触发硬件中断(IRQ)             │
│  └──────────┘                                               │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Layer 1: 中断处理层                                        │
│  ┌─────────────────┐                                        │
│  │ 硬件中断处理程序 │ → 关闭网卡中断                         │
│  └─────────────────┘ → 调度NAPI软中断(NET_RX_SOFTIRQ)       │
│                      → napi_schedule()                       │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Layer 2: NAPI轮询层 (软中断上下文)                         │
│  ┌──────────────┐                                           │
│  │net_rx_action │ → 遍历poll_list                           │
│  └──────────────┘ → 调用驱动poll函数                        │
│         ↓                                                    │
│  ┌──────────────┐                                           │
│  │driver->poll()│ → 从RX ring读取数据包                     │
│  └──────────────┘ → 分配sk_buff                             │
│                   → 调用netif_receive_skb()                 │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Layer 3: 链路层处理                                        │
│  文件: net/core/dev.c                                       │
│  ┌───────────────────────┐                                  │
│  │__netif_receive_skb()  │ → 处理VLAN标签                   │
│  └───────────────────────┘ → RPS/RFS分发                    │
│         ↓                                                    │
│  ┌───────────────────────┐                                  │
│  │eth_type_trans()       │ → 剥离以太网头                   │
│  └───────────────────────┘ → 识别协议(ETH_P_IP=0x0800)      │
│         ↓                                                    │
│  ┌───────────────────────┐                                  │
│  │协议分发(packet_type)  │ → 查找ip_packet_type              │
│  └───────────────────────┘ → 调用ip_rcv()                   │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Layer 4: 网络层(IP)处理                                    │
│  文件: net/ipv4/ip_input.c                                  │
│  ┌──────────┐                                               │
│  │ ip_rcv() │ → IP头校验                                    │
│  └──────────┘ → Netfilter PRE_ROUTING                       │
│         ↓                                                    │
│  ┌─────────────────┐                                        │
│  │ip_rcv_finish()  │ → Early Demux优化                      │
│  └─────────────────┘ → 路由决策                             │
│         ↓                                                    │
│  ┌──────────────────┐                                       │
│  │ip_local_deliver()│ → IP分片重组                          │
│  └──────────────────┘ → Netfilter LOCAL_IN                  │
│         ↓                                                    │
│  ┌────────────────────────┐                                 │
│  │ip_local_deliver_finish()│ → 协议分发(net_protocol)       │
│  └────────────────────────┘ → 查找tcp_protocol              │
│                              → 调用tcp_v4_rcv()             │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Layer 5: 传输层(TCP)处理                                   │
│  文件: net/ipv4/tcp_ipv4.c, net/ipv4/tcp_input.c            │
│  ┌──────────────┐                                           │
│  │tcp_v4_rcv()  │ → TCP头校验、校验和                       │
│  └──────────────┘ → Socket查找(__inet_lookup_skb)           │
│         ↓                                                    │
│  ┌──────────────┐                                           │
│  │tcp_v4_do_rcv│ → 锁定socket                               │
│  └──────────────┘ → 状态机处理                              │
│         ↓                                                    │
│  ┌────────────────────┐                                     │
│  │tcp_rcv_established │ → Header Prediction快速路径          │
│  └────────────────────┘ → 或慢速路径处理                    │
│         ↓                                                    │
│  ┌────────────────┐                                         │
│  │tcp_data_queue()│ → 序号检查                              │
│  └────────────────┘ → 入接收队列                            │
│                     → 处理乱序队列                          │
│                     → sk_data_ready()唤醒进程               │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Layer 6: Socket接收队列                                    │
│  ┌─────────────────────┐                                    │
│  │sk->sk_receive_queue │ (已排序的接收队列)                 │
│  └─────────────────────┘                                    │
│  ┌─────────────────────┐                                    │
│  │out_of_order_queue   │ (乱序数据包红黑树)                 │
│  └─────────────────────┘                                    │
└─────────────────────────────────────────────────────────────┘

[应用读取流程 - 自顶向下]

┌─────────────────────────────────────────────────────────────┐
│  用户空间应用                                               │
│  int n = recv(sockfd, buffer, len, flags);                  │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  系统调用层                                                 │
│  文件: net/socket.c                                         │
│  ┌──────────────────┐                                       │
│  │SYSCALL_DEFINE6   │                                       │
│  │  (recvfrom)      │ → 参数校验                            │
│  └──────────────────┘ → sockfd_lookup()                     │
│         ↓                                                    │
│  ┌──────────────────┐                                       │
│  │sock_recvmsg()    │ → 安全检查                            │
│  └──────────────────┘ → __sock_recvmsg()                    │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  Socket层                                                   │
│  文件: net/ipv4/af_inet.c                                   │
│  ┌──────────────────┐                                       │
│  │sock->ops->       │                                       │
│  │  recvmsg()       │ → inet_recvmsg()                      │
│  └──────────────────┘ → sk->sk_prot->recvmsg()              │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  TCP协议层                                                  │
│  文件: net/ipv4/tcp.c                                       │
│  ┌──────────────────┐                                       │
│  │tcp_recvmsg()     │ → lock_sock()获取锁                   │
│  └──────────────────┘                                       │
│         ↓                                                    │
│  循环处理:                                                   │
│  1. 遍历sk_receive_queue                                    │
│  2. 如果无数据: sk_wait_data()阻塞等待                      │
│  3. 找到数据包: skb_copy_datagram_msg()                     │
│  4. 更新copied_seq序号                                      │
│  5. 调整接收窗口                                            │
│  6. 释放已消费skb                                           │
│         ↓                                                    │
│  ┌──────────────────┐                                       │
│  │tcp_cleanup_rbuf()│ → 发送ACK                             │
│  └──────────────────┘ → release_sock()                      │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  数据拷贝层                                                 │
│  文件: net/core/datagram.c, lib/iovec.c                     │
│  ┌─────────────────────────┐                                │
│  │skb_copy_datagram_iovec()│ → 拷贝skb线性数据              │
│  └─────────────────────────┘ → 拷贝分页数据                │
│         ↓                                                    │
│  ┌──────────────────┐                                       │
│  │memcpy_toiovec()  │ → 遍历iovec                           │
│  └──────────────────┘                                       │
│         ↓                                                    │
│  ┌──────────────────┐                                       │
│  │copy_to_user()    │ → 内核空间→用户空间拷贝              │
│  └──────────────────┘                                       │
└─────────────────────────────────────────────────────────────┘
                      ↓
┌─────────────────────────────────────────────────────────────┐
│  用户空间缓冲区                                             │
│  数据成功拷贝到用户提供的buffer                             │
└─────────────────────────────────────────────────────────────┘

2.2 关键时间点

阶段 执行上下文 可抢占性 关键特性
硬件中断 硬中断 不可抢占 极短时间,只做必要工作
NAPI轮询 软中断 不可抢占 批量处理,有预算限制
协议栈处理 软中断 不可抢占 完整协议处理
应用读取 进程上下文 可抢占 可能阻塞等待

3. 初始化与注册机制

3.1 为什么需要初始化注册

Linux内核的TCP网络模块是一个复杂的分层系统,每一层都需要:

  • 注册入口函数:让上层知道如何调用下层
  • 注册协议处理器:建立协议类型到处理函数的映射
  • 初始化数据结构:准备哈希表、队列等核心数据结构
  • 注册中断处理:建立硬件事件到软件处理的通道

3.2 initcall机制

3.2.1 initcall优先级定义

文件 : include/linux/init.h

c 复制代码
/*
 * initcall优先级定义
 */
#define early_initcall(fn)      __define_initcall(fn, early)
#define pure_initcall(fn)       __define_initcall(fn, 0)
#define core_initcall(fn)       __define_initcall(fn, 1)
#define core_initcall_sync(fn)  __define_initcall(fn, 1s)
#define postcore_initcall(fn)   __define_initcall(fn, 2)
#define arch_initcall(fn)       __define_initcall(fn, 3)
#define subsys_initcall(fn)     __define_initcall(fn, 4)  // ← net_dev_init
#define fs_initcall(fn)         __define_initcall(fn, 5)  // ← inet_init
#define device_initcall(fn)     __define_initcall(fn, 6)  // ← 驱动模块
#define late_initcall(fn)       __define_initcall(fn, 7)
3.2.2 网络子系统初始化时间线
复制代码
内核启动时间线:
═══════════════════════════════════════════════════════════════

[启动阶段: start_kernel]
    ↓
[initcall level 4: subsys_initcall]
    ├─→ net_dev_init()                     [net/core/dev.c:8897]
    │   ├─ 初始化ptype_base哈希表
    │   ├─ 初始化per-CPU softnet_data
    │   ├─ 注册NET_RX_SOFTIRQ软中断
    │   └─ 注册NET_TX_SOFTIRQ软中断
    │
[initcall level 5: fs_initcall]
    ├─→ inet_init()                        [net/ipv4/af_inet.c:1696]
    │   ├─ proto_register(&tcp_prot)       注册TCP协议结构
    │   ├─ proto_register(&udp_prot)       注册UDP协议结构
    │   ├─ sock_register(&inet_family_ops) 注册AF_INET地址族
    │   ├─ inet_add_protocol(&tcp_protocol) 注册TCP到inet_protos[6]
    │   ├─ inet_add_protocol(&udp_protocol) 注册UDP到inet_protos[17]
    │   ├─ inet_register_protosw()         注册socket协议切换表
    │   ├─ dev_add_pack(&ip_packet_type)   注册IP到ptype_base
    │   └─ tcp_v4_init(), tcp_init()       TCP子系统初始化
    │
[initcall level 6: device_initcall]
    └─→ e1000_init_module()                [drivers/.../e1000_main.c]
        └─ pci_register_driver(&e1000_driver)
            └─ (设备探测时) e1000_probe()
                ├─ alloc_etherdev()        分配网络设备
                ├─ netif_napi_add()        注册NAPI
                ├─ register_netdev()       注册网络设备
                └─ (打开时) e1000_open()
                    └─ request_irq()       注册中断处理

═══════════════════════════════════════════════════════════════
3.2.3 核心注册表初始化状态

初始化完成后,内核中的核心注册表状态:

注册表 类型 用途 初始化函数 位置
ptype_base[] 哈希表 L2协议分发 net_dev_init net/core/dev.c:8910
inet_protos[] 数组 L3→L4协议分发 inet_init net/ipv4/af_inet.c:1744
tcp_hashinfo 哈希表 Socket查找 tcp_init net/ipv4/tcp_ipv4.c
inetsw[] 链表数组 Socket类型映射 inet_init net/ipv4/af_inet.c:1756
net_families[] 数组 地址族处理 inet_init net/socket.c

3.3 软中断层初始化

3.3.1 net_dev_init函数

文件 : net/core/dev.c:8897

initcall级别 : subsys_initcall (level 4)

c 复制代码
/**
 * net_dev_init - 网络设备子系统初始化
 * 这是网络子系统最核心的初始化函数
 */
static int __init net_dev_init(void)
{
    int i, rc = -ENOMEM;

    BUG_ON(!dev_boot_phase);

    // 1. 初始化/proc接口
    if (dev_proc_init())
        goto out;

    // 2. 初始化sysfs接口
    if (netdev_kobject_init())
        goto out;

    // 3. 初始化协议分发哈希表
    INIT_LIST_HEAD(&ptype_all);              // 全局协议链表(tcpdump用)
    for (i = 0; i < PTYPE_HASH_SIZE; i++)    // PTYPE_HASH_SIZE = 16
        INIT_LIST_HEAD(&ptype_base[i]);      // ← IP协议将注册到这里

    INIT_LIST_HEAD(&offload_base);

    // 4. 注册per-net namespace操作
    if (register_pernet_subsys(&netdev_net_ops))
        goto out;

    /*
     * 5. 初始化per-CPU接收队列 - 关键!
     */
    for_each_possible_cpu(i) {
        struct softnet_data *sd = &per_cpu(softnet_data, i);

        memset(sd, 0, sizeof(*sd));
        skb_queue_head_init(&sd->input_pkt_queue);  // 输入包队列
        skb_queue_head_init(&sd->process_queue);    // 处理队列
        sd->completion_queue = NULL;
        INIT_LIST_HEAD(&sd->poll_list);             // NAPI轮询链表
        sd->output_queue = NULL;
        sd->output_queue_tailp = &sd->output_queue;

#ifdef CONFIG_RPS
        sd->csd.func = rps_trigger_softirq;
        sd->csd.info = sd;
        sd->csd.flags = 0;
        sd->cpu = i;
#endif

        // backlog设备用于RPS
        sd->backlog.poll = process_backlog;
        sd->backlog.weight = weight_p;
        sd->backlog.gro_list = NULL;
        sd->backlog.gro_count = 0;
    }

    dev_boot_phase = 0;

    // 6. 注册loopback设备
    if (register_pernet_device(&loopback_net_ops))
        goto out;

    if (register_pernet_device(&default_device_ops))
        goto out;

    /*
     * 7. 注册软中断处理函数 - 最关键的部分!
     */
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);  // 发送软中断
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);  // 接收软中断 ← 重点

    // 8. 注册CPU热插拔回调
    hotcpu_notifier(dev_cpu_callback, 0);

    // 9. 初始化路由子系统
    dst_subsys_init();

    rc = 0;
out:
    return rc;
}

// 声明为subsys_initcall (level 4)
subsys_initcall(net_dev_init);
3.3.2 软中断注册详解

open_softirq函数 (kernel/softirq.c):

c 复制代码
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
    softirq_vec[nr].action = action;
}

注册效果:

c 复制代码
// 注册后的状态
softirq_vec[NET_RX_SOFTIRQ].action = net_rx_action;  // softirq.c中
softirq_vec[NET_TX_SOFTIRQ].action = net_tx_action;

// 软中断号定义 (include/linux/interrupt.h)
enum {
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,  // ← 网络接收软中断
    BLOCK_SOFTIRQ,
    IRQ_POLL_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,
    NR_SOFTIRQS
};

3.4 协议栈初始化

3.4.1 inet_init函数

文件 : net/ipv4/af_inet.c:1696

initcall级别 : fs_initcall (level 5)

c 复制代码
/**
 * inet_init - INET协议族初始化
 * 初始化IPv4协议栈的所有组件
 */
static int __init inet_init(void)
{
    struct inet_protosw *q;
    struct list_head *r;
    int rc = -EINVAL;

    BUILD_BUG_ON(sizeof(struct inet_skb_parm) > FIELD_SIZEOF(struct sk_buff, cb));

    // 分配本地保留端口位图
    sysctl_local_reserved_ports = kzalloc(65536 / 8, GFP_KERNEL);
    if (!sysctl_local_reserved_ports)
        goto out;

    /*
     * 1. 注册传输层协议结构 (struct proto)
     */
    rc = proto_register(&tcp_prot, 1);   // 注册TCP
    if (rc)
        goto out_free_reserved_ports;

    rc = proto_register(&udp_prot, 1);   // 注册UDP
    if (rc)
        goto out_unregister_tcp_proto;

    rc = proto_register(&raw_prot, 1);   // 注册RAW
    if (rc)
        goto out_unregister_udp_proto;

    rc = proto_register(&ping_prot, 1);  // 注册PING
    if (rc)
        goto out_unregister_raw_proto;

    /*
     * 2. 注册socket地址族
     */
    (void)sock_register(&inet_family_ops);

#ifdef CONFIG_SYSCTL
    ip_static_sysctl_init();
#endif

    tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;

    /*
     * 3. 注册网络层协议处理 (struct net_protocol)
     */
    if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
        pr_crit("%s: Cannot add ICMP protocol\n", __func__);
    if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
        pr_crit("%s: Cannot add UDP protocol\n", __func__);
    if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)  // ← 注册TCP
        pr_crit("%s: Cannot add TCP protocol\n", __func__);
#ifdef CONFIG_IP_MULTICAST
    if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
        pr_crit("%s: Cannot add IGMP protocol\n", __func__);
#endif

    /*
     * 4. 注册socket协议切换表
     */
    for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
        INIT_LIST_HEAD(r);

    for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
        inet_register_protosw(q);

    /*
     * 5. 初始化各子系统
     */
    arp_init();                          // ARP协议
    ip_init();                           // IP层
    tcp_v4_init();                       // TCP IPv4
    tcp_init();                          // TCP核心
    udp_init();                          // UDP
    udplite4_register();                 // UDP-Lite
    ping_init();                         // PING

    if (icmp_init() < 0)
        panic("Failed to create the ICMP control socket.\n");

#if defined(CONFIG_IP_MROUTE)
    if (ip_mr_init())
        pr_crit("%s: Cannot init ipv4 mroute\n", __func__);
#endif

    if (init_ipv4_mibs())
        pr_crit("%s: Cannot init ipv4 mibs\n", __func__);

    ipv4_proc_init();
    ipfrag_init();

    /*
     * 6. 注册链路层协议处理 (struct packet_type)
     */
    dev_add_pack(&ip_packet_type);       // ← 注册IP到ptype_base

    ip_tunnel_core_init();

    rc = 0;
out:
    return rc;

// ... 错误处理代码省略 ...
}

// 声明为fs_initcall (level 5)
fs_initcall(inet_init);
3.4.2 协议注册详解

IP协议注册 (net/ipv4/af_inet.c:1691):

c 复制代码
static struct packet_type ip_packet_type __read_mostly = {
    .type = cpu_to_be16(ETH_P_IP),  // 0x0800
    .func = ip_rcv,                  // 接收函数
};

// 在inet_init()中注册:
dev_add_pack(&ip_packet_type);

TCP协议注册 (net/ipv4/af_inet.c:1557):

c 复制代码
static const struct net_protocol tcp_protocol = {
    .early_demux    = tcp_v4_early_demux,
    .handler        = tcp_v4_rcv,
    .err_handler    = tcp_v4_err,
    .no_policy      = 1,
    .netns_ok       = 1,
};

// 在inet_init()中注册:
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
    pr_crit("%s: Cannot add TCP protocol\n", __func__);

协议数组:

c 复制代码
const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
// MAX_INET_PROTOS = 256
// 索引是IP协议号:
//   IPPROTO_TCP = 6
//   IPPROTO_UDP = 17
//   IPPROTO_ICMP = 1

3.5 网卡驱动初始化

3.5.1 驱动模块加载流程(以e1000为例)

文件 : drivers/net/ethernet/intel/e1000/e1000_main.c

模块入口 (第248行):

c 复制代码
/**
 * e1000_init_module - 驱动初始化入口点
 */
static int __init e1000_init_module(void)
{
    int ret;
    pr_info("%s - version %s\n", e1000_driver_string, e1000_driver_version);
    pr_info("%s\n", e1000_copyright);

    // 注册PCI驱动 - 第255行
    ret = pci_register_driver(&e1000_driver);

    return ret;
}

// 第266行: 声明为module_init (可加载模块) 或 device_initcall (编译进内核)
module_init(e1000_init_module);
// 第279行: 模块卸载函数
module_exit(e1000_exit_module);

PCI驱动结构:

c 复制代码
/**
 * e1000_driver - PCI驱动结构
 */
static struct pci_driver e1000_driver = {
    .name     = e1000_driver_name,    // "e1000"
    .id_table = e1000_pci_tbl,        // 支持的设备ID列表
    .probe    = e1000_probe,          // 设备探测函数 ← 关键
    .remove   = e1000_remove,         // 设备移除函数
    .driver   = {
        .pm = &e1000_pm_ops,          // 电源管理
    },
    .shutdown = e1000_shutdown,
    .err_handler = &e1000_err_handler
};
3.5.2 设备探测函数 (e1000_probe)

位置 : drivers/net/ethernet/intel/e1000/e1000_main.c:946-1277

当PCI总线发现匹配的Intel 82540/82545/82546网卡时,调用此函数:

c 复制代码
static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
    struct net_device *netdev;
    struct e1000_adapter *adapter;
    struct e1000_hw *hw;
    int err;

    // 1. 启用PCI设备 (965-968行)
    err = pci_enable_device(pdev);

    // 2. 请求PCI区域 (973行)
    err = pci_request_selected_regions(pdev, bars, e1000_driver_name);

    // 3. 分配网络设备结构 (983行)
    netdev = alloc_etherdev(sizeof(struct e1000_adapter));

    // 4. 初始化硬件结构 (1017行)
    err = e1000_init_hw_struct(adapter, hw);

    // 5. 设置网络设备操作函数集 (1042行)
    netdev->netdev_ops = &e1000_netdev_ops;

    // 6. ★ 注册NAPI ★ (1045行) - 关键初始化
    netif_napi_add(netdev,          // 网络设备
                   &adapter->napi,  // NAPI结构体指针
                   e1000_clean,     // 轮询函数
                   64);             // 预算(budget)

    // 7. 软件初始化 (1053行)
    err = e1000_sw_init(adapter);

    // 8. 重置硬件 (1228行)
    e1000_reset(adapter);

    // 9. ★ 注册网络设备 ★ (1231行) - 关键注册
    err = register_netdev(netdev);

    // 10. 设置载波状态为离线 (1248行)
    netif_carrier_off(netdev);

    // 11. 增加已发现设备计数 (1252行)
    cards_found++;

    return 0;
}
3.5.3 网络接口打开 (e1000_open)

位置 : drivers/net/ethernet/intel/e1000/e1000_main.c:1384-1448

用户执行ifconfig eth0 upip link set eth0 up时调用:

c 复制代码
static int e1000_open(struct net_device *netdev)
{
    struct e1000_adapter *adapter = netdev_priv(netdev);
    int err;

    // 1. 分配TX资源 (1397行)
    err = e1000_setup_all_tx_resources(adapter);

    // 2. 分配RX资源 (1402行)
    err = e1000_setup_all_rx_resources(adapter);

    // 3. 配置硬件 (1419行)
    e1000_configure(adapter);

    // 4. ★ 注册中断处理程序 ★ (1421行) - 最关键
    err = e1000_request_irq(adapter);

    // 5. 清除DOWN标志 (1426行)
    clear_bit(__E1000_DOWN, &adapter->flags);

    // 6. ★ 启用NAPI ★ (1428行)
    napi_enable(&adapter->napi);

    // 7. ★ 启用硬件中断 ★ (1430行)
    e1000_irq_enable(adapter);

    // 8. 启动发送队列 (1432行)
    netif_start_queue(netdev);

    // 9. 触发链路状态改变中断 (1435行)
    ew32(ICS, E1000_ICS_LSC);

    return 0;
}
3.5.4 中断注册 (e1000_request_irq)

位置 : drivers/net/ethernet/intel/e1000/e1000_main.c:281-295

c 复制代码
static int e1000_request_irq(struct e1000_adapter *adapter)
{
    struct net_device *netdev = adapter->netdev;
    irq_handler_t handler = e1000_intr;      // 中断处理函数指针
    int irq_flags = IRQF_SHARED;             // 共享中断标志
    int err;

    // 第288行: ★ 注册中断 ★
    err = request_irq(adapter->pdev->irq,    // PCI设备的IRQ号
                     handler,                 // e1000_intr 中断处理函数
                     irq_flags,               // IRQF_SHARED
                     netdev->name,            // 设备名 "eth0"
                     netdev);                 // 传递给handler的私有数据
    if (err) {
        e_err(probe, "Unable to allocate interrupt Error: %d\n", err);
    }

    return err;
}

request_irq效果:

复制代码
/proc/interrupts 中会出现:
  19:    1234567   IO-APIC-fasteoi   eth0

中断向量表中的处理函数:
  IRQ[19] -> e1000_intr()

3.6 完整初始化调用链

复制代码
═══════════════════════════════════════════════════════════
                    E1000驱动初始化完整流程
═══════════════════════════════════════════════════════════

[内核启动 - initcall level 6: device_initcall]
    ↓
e1000_init_module() [248行]
    ↓
pci_register_driver(&e1000_driver) [255行]
    ↓
[PCI总线探测到82540/82545/82546网卡]
    ↓
e1000_probe() [946行]
    ├─ pci_enable_device() [965行]
    ├─ alloc_etherdev() [983行]
    ├─ e1000_init_hw_struct() [1017行]
    ├─ netdev->netdev_ops = &e1000_netdev_ops [1042行]
    │
    ├─ ★ netif_napi_add(netdev, &adapter->napi, e1000_clean, 64) ★ [1045行]
    │   └─ 注册NAPI结构体和轮询函数
    │
    ├─ e1000_sw_init() [1053行]
    ├─ e1000_reset() [1228行]
    │
    └─ ★ register_netdev(netdev) ★ [1231行]
        └─ 设备在/sys和/proc中可见
            ↓
[用户执行: ip link set eth0 up]
    ↓
e1000_open() [1384行]
    ├─ e1000_setup_all_tx_resources() [1397行]
    ├─ e1000_setup_all_rx_resources() [1402行]
    ├─ e1000_configure() [1419行]
    │
    ├─ ★ e1000_request_irq() ★ [1421行]
    │   └─ request_irq(pdev->irq, e1000_intr, ...) [288行]
    │       └─ 注册中断向量: IRQ[N] -> e1000_intr
    │
    ├─ ★ napi_enable(&adapter->napi) ★ [1428行]
    └─ ★ e1000_irq_enable() ★ [1430行]

═══════════════════════════════════════════════════════════

4. 网卡驱动层分析

4.1 核心数据结构

4.1.1 struct net_device (网络设备)

文件 : include/linux/netdevice.h:1675

c 复制代码
struct net_device {
    char            name[IFNAMSIZ];        // 设备名称 (如"eth0")

    // 硬件参数
    unsigned long   mem_end;               // 共享内存结束地址
    unsigned long   mem_start;             // 共享内存起始地址
    unsigned long   base_addr;             // 设备I/O地址
    unsigned int    irq;                   // 设备IRQ号

    unsigned long   state;                 // 设备状态

    // NAPI相关
    struct list_head    napi_list;         // NAPI结构链表

    // 设备特性
    netdev_features_t   features;          // 当前激活特性
    netdev_features_t   hw_features;       // 硬件支持特性

    int             ifindex;               // 接口索引(唯一标识)

    struct net_device_stats stats;         // 统计信息
    atomic_long_t   rx_dropped;            // 丢弃的接收包

    // 操作函数集
    const struct net_device_ops *netdev_ops;  // 设备操作
    const struct ethtool_ops *ethtool_ops;    // 管理工具接口
    const struct header_ops *header_ops;      // 头部操作

    unsigned int    flags;                 // 接口标志 (IFF_UP等)
    unsigned int    mtu;                   // 最大传输单元
    unsigned short  type;                  // 硬件类型 (ARPHRD_ETHER等)
    unsigned short  hard_header_len;       // 硬件头长度

    unsigned char   *dev_addr;             // MAC地址
    unsigned char   broadcast[MAX_ADDR_LEN]; // 广播地址

    // 发送队列
    struct netdev_queue *_tx;              // 发送队列数组
    unsigned int    num_tx_queues;         // 发送队列数量

    // 接收相关
    unsigned int    real_num_rx_queues;    // 实际RX队列数

    // GRO相关
    struct list_head    dev_list;          // 全局设备链表
};

关键字段说明:

  • napi_list: 该设备上所有NAPI实例链表
  • netdev_ops: 包含设备打开、关闭、发送等操作函数指针
  • irq: 硬件中断号,用于注册中断处理程序
4.1.2 struct napi_struct (NAPI轮询结构)

文件 : include/linux/netdevice.h:323

c 复制代码
struct napi_struct {
    /* poll_list只能被改变NAPI_STATE_SCHED位的实体管理 */
    struct list_head    poll_list;         // 全局轮询链表节点

    unsigned long       state;             // NAPI状态位
    int                 weight;            // 轮询权重(预算)
    unsigned int        gro_count;         // GRO计数

    int (*poll)(struct napi_struct *, int); // 轮询函数(驱动实现)

    struct net_device   *dev;              // 关联的网络设备
    struct sk_buff      *gro_list;         // GRO包链表
    struct sk_buff      *skb;              // 当前处理的skb

    struct list_head    dev_list;          // 设备上的NAPI链表节点
    struct hlist_node   napi_hash_node;    // NAPI哈希表节点
    unsigned int        napi_id;           // NAPI ID (用于busy poll)
};

NAPI状态位 (include/linux/netdevice.h:355):

c 复制代码
enum {
    NAPI_STATE_SCHED,        // Poll已调度
    NAPI_STATE_DISABLE,      // Disable挂起
    NAPI_STATE_NPSVC,        // Netpoll - 不从poll_list出队
    NAPI_STATE_HASHED,       // 在NAPI哈希表中
    NAPI_STATE_NO_BUSY_POLL, // 不加入napi_hash,无busy polling
    NAPI_STATE_IN_BUSY_POLL, // sk_busy_loop()拥有此NAPI
    NAPI_STATE_MISSED,       // 需要重新调度
};

关键函数:

c 复制代码
// 调度NAPI轮询
void napi_schedule(struct napi_struct *n);

// NAPI轮询完成
bool napi_complete_done(struct napi_struct *n, int work_done);

// 注册NAPI
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
                   int (*poll)(struct napi_struct *, int), int weight);
4.1.3 struct sk_buff (Socket缓冲区)

文件 : include/linux/skbuff.h:667

这是Linux网络栈最核心的数据结构,代表一个网络数据包。

c 复制代码
struct sk_buff {
    union {
        struct {
            struct sk_buff  *next;         // 链表下一个
            struct sk_buff  *prev;         // 链表上一个

            union {
                ktime_t     tstamp;        // 时间戳
                struct skb_mstamp skb_mstamp;
            };
        };
        struct rb_node  rbnode;            // 红黑树节点(TCP乱序队列)
    };

    struct sock         *sk;               // 所属socket
    struct net_device   *dev;              // 到达/离开的设备

    char                cb[48] __aligned(8); // 控制缓冲区(每层私有)

    unsigned long       _skb_refdst;       // 目的路由项

    unsigned int        len;               // 实际数据长度
    unsigned int        data_len;          // 数据长度
    __u16               mac_len;           // MAC头长度
    __u16               hdr_len;           // 可写头长度

    // 校验和相关
    union {
        __wsum          csum;
        struct {
            __u16       csum_start;
            __u16       csum_offset;
        };
    };

    __u32               priority;          // 包优先级

    // 标志位
    __u8                cloned:1;          // 头部被克隆
    __u8                ip_summed:2;       // 校验和状态
    __u8                nohdr:1;           // 只引用负载
    __u8                pkt_type:3;        // 包类型(单播/广播/组播)

    __be16              protocol;          // 协议(ETH_P_IP等)

    void                (*destructor)(struct sk_buff *skb);

    // Netfilter连接跟踪
    struct nf_conntrack *nfct;

    int                 skb_iif;           // 入接口索引

    __u32               hash;              // 包哈希值
    __u16               queue_mapping;     // 队列映射

    // 各层头部指针
    sk_buff_data_t      transport_header;  // 传输层头
    sk_buff_data_t      network_header;    // 网络层头
    sk_buff_data_t      mac_header;        // MAC层头

    sk_buff_data_t      tail;              // 尾指针
    sk_buff_data_t      end;               // 结束指针
    unsigned char       *head;             // 头部缓冲区起始
    unsigned char       *data;             // 数据起始指针

    unsigned int        truesize;          // 缓冲区总大小
    atomic_t            users;             // 引用计数
};

sk_buff内存布局:

复制代码
         head                    data              tail        end
          ↓                       ↓                 ↓           ↓
          +-------+---------------+-----------------+-----------+
          |headroom|   headers    |    payload      | tailroom  |
          +-------+---------------+-----------------+-----------+
                   ↑               ↑                 ↑
              mac_header    network_header   transport_header
                           (IP header)       (TCP header)

skb_shared_info (位于skb->end):

c 复制代码
struct skb_shared_info {
    unsigned char   nr_frags;              // 分片数量
    __u8            tx_flags;              // 发送标志
    unsigned short  gso_size;              // GSO段大小
    unsigned short  gso_segs;              // GSO段数
    unsigned short  gso_type;              // GSO类型
    struct sk_buff  *frag_list;            // 分片链表
    atomic_t        dataref;               // 数据引用计数
    skb_frag_t      frags[MAX_SKB_FRAGS];  // 页面分片数组
};

4.2 硬件中断处理

4.2.1 中断处理流程
复制代码
硬件网卡接收到数据包
    ↓
DMA将数据写入Ring Buffer
    ↓
网卡产生硬件中断(IRQ)
    ↓
CPU跳转到中断向量表
    ↓
调用网卡驱动的中断处理函数
    ↓
【中断处理程序执行】
    ├─ 读取中断状态寄存器
    ├─ 确认是RX中断
    ├─ 禁用网卡中断(避免中断风暴)
    ├─ 调用napi_schedule()调度软中断
    └─ 返回(IRET)

典型驱动中断处理函数示例 (e1000驱动):

c 复制代码
static irqreturn_t e1000_intr(int irq, void *data)
{
    struct net_device *netdev = data;
    struct e1000_adapter *adapter = netdev_priv(netdev);
    struct e1000_hw *hw = &adapter->hw;
    u32 icr = er32(ICR);  // 读取中断原因寄存器

    if (!icr)
        return IRQ_NONE;  // 不是我们的中断

    // 关键:关闭网卡中断,避免中断风暴
    if (!(icr & E1000_ICR_INT_ASSERTED))
        return IRQ_NONE;

    // 调度NAPI软中断
    if (napi_schedule_prep(&adapter->napi)) {
        adapter->total_tx_bytes = 0;
        adapter->total_tx_packets = 0;
        adapter->total_rx_bytes = 0;
        adapter->total_rx_packets = 0;
        __napi_schedule(&adapter->napi);
    }

    return IRQ_HANDLED;
}
4.2.2 NAPI调度

napi_schedule() (include/linux/netdevice.h:452):

c 复制代码
static inline void napi_schedule(struct napi_struct *n)
{
    if (napi_schedule_prep(n))  // 检查是否可以调度
        __napi_schedule(n);
}

bool napi_schedule_prep(struct napi_struct *n)
{
    // 原子地设置NAPI_STATE_SCHED位
    return !napi_disable_pending(n) &&
           !test_and_set_bit(NAPI_STATE_SCHED, &n->state);
}

__napi_schedule() (net/core/dev.c):

c 复制代码
void __napi_schedule(struct napi_struct *n)
{
    unsigned long flags;

    local_irq_save(flags);

    // 将napi添加到当前CPU的softnet_data的poll_list
    list_add_tail(&n->poll_list, &__get_cpu_var(softnet_data).poll_list);

    // 触发NET_RX_SOFTIRQ软中断
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);

    local_irq_restore(flags);
}

4.3 NAPI软中断处理

4.3.1 软中断初始化

net_dev_init() (net/core/dev.c):

c 复制代码
static int __init net_dev_init(void)
{
    // ...

    // 注册NET_RX_SOFTIRQ软中断处理函数
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);

    // 注册NET_TX_SOFTIRQ软中断处理函数
    open_softirq(NET_TX_SOFTIRQ, net_tx_action);

    // ...
}
subsys_initcall(net_dev_init);
4.3.2 软中断处理函数

net_rx_action() (net/core/dev.c:5503):

c 复制代码
static void net_rx_action(struct softirq_action *h)
{
    struct softnet_data *sd = this_cpu_ptr(&softnet_data);
    unsigned long time_limit = jiffies + 2;  // 时间限制:2个jiffy
    int budget = netdev_budget;              // 预算:默认300个包
    LIST_HEAD(list);
    LIST_HEAD(repoll);

    local_irq_disable();
    // 取出当前CPU的poll_list
    list_splice_init(&sd->poll_list, &list);
    local_irq_enable();

    for (;;) {
        struct napi_struct *n;

        if (list_empty(&list)) {
            if (!sd_has_rps_ipi_waiting(sd) && list_empty(&repoll))
                return;
            break;
        }

        // 取出第一个NAPI实例
        n = list_first_entry(&list, struct napi_struct, poll_list);

        // 调用napi_poll(),返回消耗的预算
        budget -= napi_poll(n, &repoll);

        // 检查是否超出限制
        if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit))) {
            sd->time_squeeze++;  // 记录被挤压次数
            break;
        }
    }

    __kfree_skb_flush();

    local_irq_disable();

    // 重新调度未处理完的NAPI
    list_splice_tail_init(&sd->poll_list, &list);
    list_splice_tail(&repoll, &list);
    list_splice(&list, &sd->poll_list);
    if (!list_empty(&sd->poll_list))
        __raise_softirq_irqoff(NET_RX_SOFTIRQ);

    net_rps_action_and_irq_enable(sd);
}

关键参数:

  • netdev_budget: 单次软中断最多处理的包数(默认300)
  • time_limit: 时间限制(2个jiffies,通常2-4ms)
  • 超出限制会记录time_squeeze并重新调度
4.3.3 NAPI Poll处理

napi_poll() (net/core/dev.c):

c 复制代码
static int napi_poll(struct napi_struct *n, struct list_head *repoll)
{
    void *have;
    int work, weight;

    list_del_init(&n->poll_list);  // 从poll_list移除

    have = netpoll_poll_lock(n);

    weight = n->weight;  // 获取权重(预算)

    // 调用驱动提供的poll函数
    work = 0;
    if (test_bit(NAPI_STATE_SCHED, &n->state)) {
        work = n->poll(n, weight);  // ← 调用驱动的poll函数
        trace_napi_poll(n, work, weight);
    }

    WARN_ON_ONCE(work > weight);

    if (likely(work < weight)) {
        // 工作完成,完成NAPI
        napi_complete_done(n, work);
    } else {
        // 还有工作,重新调度
        if (unlikely(napi_disable_pending(n))) {
            napi_complete(n);
        } else {
            list_add_tail(&n->poll_list, repoll);
        }
    }

    netpoll_poll_unlock(have);

    return work;
}

驱动poll函数示例:

c 复制代码
static int e1000_clean(struct napi_struct *napi, int budget)
{
    struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter, napi);
    int work_done = 0;

    // 处理接收队列
    e1000_clean_rx_irq(adapter, &work_done, budget);

    // 如果处理完了所有包
    if (work_done < budget) {
        napi_complete(napi);  // 完成NAPI

        // 重新开启网卡中断
        if (likely(adapter->itr_setting & 3))
            e1000_set_itr(adapter);
        e1000_irq_enable(adapter);
    }

    return work_done;
}

4.4 数据包接收

e1000_clean_rx_irq() (驱动示例):

c 复制代码
static bool e1000_clean_rx_irq(struct e1000_adapter *adapter,
                               int *work_done, int work_to_do)
{
    struct net_device *netdev = adapter->netdev;
    struct pci_dev *pdev = adapter->pdev;
    struct e1000_rx_ring *rx_ring = adapter->rx_ring;
    struct e1000_rx_desc *rx_desc;
    struct sk_buff *skb;
    unsigned int i;
    int cleaned_count = 0;
    bool cleaned = false;

    i = rx_ring->next_to_clean;
    rx_desc = E1000_RX_DESC(*rx_ring, i);

    while (rx_desc->status & E1000_RXD_STAT_DD) {  // 检查描述符完成位
        if (*work_done >= work_to_do)
            break;
        (*work_done)++;

        rmb();  // 读内存屏障

        // 分配sk_buff
        skb = netdev_alloc_skb(netdev, length + NET_IP_ALIGN);

        // 从RX ring拷贝数据到skb
        skb_put(skb, length);
        memcpy(skb->data, buffer_info->rxbuf.data, length);

        // 设置skb字段
        skb->protocol = eth_type_trans(skb, netdev);

        // 提交给协议栈
        netif_receive_skb(skb);

        // 更新统计
        netdev->stats.rx_packets++;
        netdev->stats.rx_bytes += length;

        // 移动到下一个描述符
        i++;
        if (i == rx_ring->count)
            i = 0;
        rx_desc = E1000_RX_DESC(*rx_ring, i);

        cleaned = true;
        cleaned_count++;
    }

    rx_ring->next_to_clean = i;

    // 分配新的RX buffer
    if (cleaned_count)
        e1000_alloc_rx_buffers(adapter, rx_ring, cleaned_count);

    return cleaned;
}

关键步骤:

  1. 从RX Ring读取描述符,检查DD位(描述符完成)
  2. 分配sk_buff结构
  3. DMA数据拷贝到sk_buff
  4. 调用eth_type_trans()设置协议
  5. 调用netif_receive_skb()提交到协议栈
  6. 补充新的RX buffer给硬件

4.5 提交到协议栈

netif_receive_skb() (net/core/dev.c:4451):

c 复制代码
int netif_receive_skb(struct sk_buff *skb)
{
    int ret;

    net_timestamp_check(netdev_tstamp_prequeue, skb);

    if (skb_defer_rx_timestamp(skb))
        return NET_RX_SUCCESS;

    rcu_read_lock();

#ifdef CONFIG_RPS
    if (static_key_false(&rps_needed)) {
        struct rps_dev_flow voidflow, *rflow = &voidflow;
        int cpu = get_rps_cpu(skb->dev, skb, &rflow);

        if (cpu >= 0) {
            ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
            rcu_read_unlock();
            return ret;
        }
    }
#endif

    ret = __netif_receive_skb(skb);
    rcu_read_unlock();
    return ret;
}

RPS (Receive Packet Steering): 如果启用,将数据包分发到不同CPU处理,提高并行性。


5. 网络协议栈层分析

5.1 链路层(L2)处理

5.1.1 入口函数

__netif_receive_skb() (net/core/dev.c:4383):

c 复制代码
static int __netif_receive_skb(struct sk_buff *skb)
{
    int ret;

    if (sk_memalloc_socks() && skb_pfmemalloc(skb)) {
        unsigned long pflags = current->flags;

        current->flags |= PF_MEMALLOC;
        ret = __netif_receive_skb_core(skb, true);
        tsk_restore_flags(current, pflags, PF_MEMALLOC);
    } else
        ret = __netif_receive_skb_core(skb, false);

    return ret;
}
5.1.2 核心处理函数

__netif_receive_skb_core() (net/core/dev.c:4237):

c 复制代码
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
    struct packet_type *ptype, *pt_prev;
    rx_handler_func_t *rx_handler;
    struct net_device *orig_dev;
    bool deliver_exact = false;
    int ret = NET_RX_DROP;
    __be16 type;

    net_timestamp_check(!netdev_tstamp_prequeue, skb);

    trace_netif_receive_skb(skb);

    // 记录原始设备
    orig_dev = skb->dev;

    // 重置网络头
    skb_reset_network_header(skb);
    if (!skb_transport_header_was_set(skb))
        skb_reset_transport_header(skb);

    // 修剪skb
    skb_reset_mac_len(skb);

pt_previous:
    skb->skb_iif = skb->dev->ifindex;

    __this_cpu_inc(softnet_data.processed);

    // 处理VLAN标签
    if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
        skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
        skb = skb_vlan_untag(skb);
        if (unlikely(!skb))
            goto out;
    }

#ifdef CONFIG_NET_CLS_ACT
    // 处理TC ingress
    if (skb->tc_verd & TC_NCLS) {
        skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);
        goto ncls;
    }
#endif

    if (pfmemalloc)
        goto skip_taps;

    // 处理抓包(tcpdump等)
    list_for_each_entry_rcu(ptype, &ptype_all, list) {
        if (!ptype->dev || ptype->dev == skb->dev) {
            if (pt_prev)
                ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = ptype;
        }
    }

skip_taps:
#ifdef CONFIG_NET_CLS_ACT
    skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
    if (!skb)
        goto out;
ncls:
#endif

    if (pfmemalloc && !skb_pfmemalloc_protocol(skb))
        goto drop;

    // VLAN处理
    if (skb_vlan_tag_present(skb)) {
        if (pt_prev) {
            ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = NULL;
        }
        if (vlan_do_receive(&skb))
            goto another_round;
        else if (unlikely(!skb))
            goto out;
    }

    // RX handler (用于bonding, bridge等)
    rx_handler = rcu_dereference(skb->dev->rx_handler);
    if (rx_handler) {
        if (pt_prev) {
            ret = deliver_skb(skb, pt_prev, orig_dev);
            pt_prev = NULL;
        }
        switch (rx_handler(&skb)) {
        case RX_HANDLER_CONSUMED:
            ret = NET_RX_SUCCESS;
            goto out;
        case RX_HANDLER_ANOTHER:
            goto another_round;
        case RX_HANDLER_EXACT:
            deliver_exact = true;
        case RX_HANDLER_PASS:
            break;
        default:
            BUG();
        }
    }

    // 处理macvlan
    if (unlikely(skb_vlan_tag_present(skb))) {
        if (skb_vlan_tag_get_id(skb))
            skb->pkt_type = PACKET_OTHERHOST;
        skb->vlan_tci = 0;
    }

    // 协议分发 - 关键!
    type = skb->protocol;

    // 精确匹配:特定设备+协议
    deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
                          &orig_dev->ptype_specific);

    // 通配匹配:协议
    if (unlikely(skb->dev != orig_dev)) {
        deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
                              &skb->dev->ptype_specific);
    }

    // 从全局ptype_base哈希表查找
    deliver_ptype_list_skb(skb, &pt_prev, orig_dev, type,
                          &ptype_base[ntohs(type) & PTYPE_HASH_MASK]);

    // 调用协议处理函数
    if (pt_prev) {
        if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
            goto drop;
        else
            ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
    } else {
drop:
        atomic_long_inc(&skb->dev->rx_dropped);
        kfree_skb(skb);
        ret = NET_RX_DROP;
    }

out:
    return ret;
}
5.1.3 以太网类型转换

eth_type_trans() (net/ethernet/eth.c:193):

c 复制代码
__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
    struct ethhdr *eth;

    skb->dev = dev;
    skb_reset_mac_header(skb);
    skb_pull_inline(skb, ETH_HLEN);  // 剥离以太网头(14字节)
    eth = eth_hdr(skb);

    // 确定包类型
    if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
        if (ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
            skb->pkt_type = PACKET_BROADCAST;  // 广播
        else
            skb->pkt_type = PACKET_MULTICAST;  // 组播
    } else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, dev->dev_addr)))
        skb->pkt_type = PACKET_OTHERHOST;     // 其他主机(promiscuous模式)

    // 返回协议类型
    // 对于IPv4: ETH_P_IP = 0x0800
    // 对于IPv6: ETH_P_IPV6 = 0x86DD
    // 对于ARP:  ETH_P_ARP = 0x0806
    if (likely(eth_proto_is_802_3(eth->h_proto)))
        return eth->h_proto;

    // 802.2 LLC
    return htons(ETH_P_802_2);
}
5.1.4 协议分发机制

packet_type结构 (include/linux/netdevice.h:2382):

c 复制代码
struct packet_type {
    __be16          type;        // 协议类型 (ETH_P_IP = 0x0800)
    struct net_device  *dev;     // NULL表示所有设备
    int             (*func) (struct sk_buff *,
                            struct net_device *,
                            struct packet_type *,
                            struct net_device *);
    bool            (*id_match)(struct packet_type *ptype,
                               struct sock *sk);
    void            *af_packet_priv;
    struct list_head    list;
};

IP协议注册 (net/ipv4/af_inet.c:1691):

c 复制代码
static struct packet_type ip_packet_type __read_mostly = {
    .type = cpu_to_be16(ETH_P_IP),  // 0x0800
    .func = ip_rcv,                  // 接收函数
};

// 在inet_init()中注册:
dev_add_pack(&ip_packet_type);

协议哈希表:

c 复制代码
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
// PTYPE_HASH_SIZE = 16
// 哈希函数: ntohs(type) & PTYPE_HASH_MASK
// 对于IP: ntohs(0x0800) & 15 = 2048 & 15 = 0

deliver_ptype_list_skb() (net/core/dev.c:1957):

c 复制代码
static inline void deliver_ptype_list_skb(struct sk_buff *skb,
                                         struct packet_type **pt,
                                         struct net_device *orig_dev,
                                         __be16 type,
                                         struct list_head *ptype_list)
{
    struct packet_type *ptype, *pt_prev = *pt;

    list_for_each_entry_rcu(ptype, ptype_list, list) {
        if (ptype->type != type)
            continue;
        if (pt_prev)
            deliver_skb(skb, pt_prev, orig_dev);
        pt_prev = ptype;
    }
    *pt = pt_prev;
}

static inline int deliver_skb(struct sk_buff *skb,
                             struct packet_type *pt_prev,
                             struct net_device *orig_dev)
{
    if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
        return -ENOMEM;
    atomic_inc(&skb->users);  // 增加引用计数
    return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}

5.2 网络层(L3) - IP处理

5.2.1 IP接收入口

ip_rcv() (net/ipv4/ip_input.c:382):

c 复制代码
int ip_rcv(struct sk_buff *skb, struct net_device *dev,
          struct packet_type *pt, struct net_device *orig_dev)
{
    const struct iphdr *iph;
    u32 len;
    struct net *net;

    net = dev_net(dev);

    // 快速路径检查
    if (skb->pkt_type == PACKET_OTHERHOST)
        goto drop;

    IP_UPD_PO_STATS_BH(net, IPSTATS_MIB_IN, skb->len);

    // 确保包含完整IP头
    if (!pskb_may_pull(skb, sizeof(struct iphdr)))
        goto inhdr_error;

    iph = ip_hdr(skb);

    /*
     * RFC1122: 3.2.1.2 验证IP头
     * - 版本号必须是4
     * - IP头长度 >= 20字节
     * - 包长度 >= IP头长度
     */
    if (iph->ihl < 5 || iph->version != 4)
        goto inhdr_error;

    BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
    BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
    BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
    IP_ADD_STATS_BH(net,
                    IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
                    max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));

    if (!pskb_may_pull(skb, iph->ihl*4))
        goto inhdr_error;

    iph = ip_hdr(skb);

    // 验证IP头校验和
    if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
        goto csum_error;

    len = ntohs(iph->tot_len);
    if (skb->len < len) {
        IP_INC_STATS_BH(net, IPSTATS_MIB_INTRUNCATEDPKTS);
        goto drop;
    } else if (len < (iph->ihl*4))
        goto inhdr_error;

    // 修剪填充
    if (pskb_trim_rcsum(skb, len)) {
        IP_INC_STATS_BH(net, IPSTATS_MIB_INDISCARDS);
        goto drop;
    }

    skb->transport_header = skb->network_header + iph->ihl*4;

    // 移除任何碎片控制块
    memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));

    /* 必须在此之后丢弃skb, 如果skb->dst被使用 */
    nf_reset(skb);

    // Netfilter钩子: PRE_ROUTING
    return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
                   net, NULL, skb, dev, NULL,
                   ip_rcv_finish);

csum_error:
    IP_INC_STATS_BH(net, IPSTATS_MIB_CSUMERRORS);
inhdr_error:
    IP_INC_STATS_BH(net, IPSTATS_MIB_INHDRERRORS);
drop:
    kfree_skb(skb);
out:
    return NET_RX_DROP;
}
5.2.2 IP接收完成

ip_rcv_finish() (net/ipv4/ip_input.c:314):

c 复制代码
static int ip_rcv_finish(struct sock *sk, struct sk_buff *skb)
{
    const struct iphdr *iph = ip_hdr(skb);
    struct rtable *rt;
    struct net *net = dev_net(skb->dev);

    /*
     * Early Demux优化
     * 对于已建立的连接,提前进行socket查找和路由缓存
     */
    if (sysctl_ip_early_demux && !skb_dst(skb) && !skb->sk) {
        const struct net_protocol *ipprot;
        int protocol = iph->protocol;

        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot && ipprot->early_demux) {
            ipprot->early_demux(skb);  // 调用tcp_v4_early_demux()
            /* 重新获取,因为early_demux可能改变 */
            iph = ip_hdr(skb);
        }
    }

    /*
     * 路由查找
     * 决定包的去向:本地交付还是转发
     */
    if (!skb_valid_dst(skb)) {
        int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
                                       iph->tos, skb->dev);
        if (unlikely(err)) {
            if (err == -EXDEV)
                NET_INC_STATS_BH(net, LINUX_MIB_IPRPFILTER);
            goto drop;
        }
    }

#ifdef CONFIG_IP_ROUTE_CLASSID
    if (unlikely(skb_dst(skb)->tclassid)) {
        struct ip_rt_acct *st = this_cpu_ptr(ip_rt_acct);
        u32 idx = skb_dst(skb)->tclassid;
        st[idx&0xFF].o_packets++;
        st[idx&0xFF].o_bytes += skb->len;
        st[(idx>>16)&0xFF].i_packets++;
        st[(idx>>16)&0xFF].i_bytes += skb->len;
    }
#endif

    // IP选项处理
    if (iph->ihl > 5 && ip_rcv_options(skb))
        goto drop;

    rt = skb_rtable(skb);
    if (rt->rt_type == RTN_MULTICAST) {
        IP_UPD_PO_STATS_BH(net, IPSTATS_MIB_INMCAST, skb->len);
    } else if (rt->rt_type == RTN_BROADCAST)
        IP_UPD_PO_STATS_BH(net, IPSTATS_MIB_INBCAST, skb->len);

    // 调用路由表的input函数
    // 对于本地包: ip_local_deliver
    // 对于转发包: ip_forward
    return dst_input(skb);

drop:
    kfree_skb(skb);
    return NET_RX_DROP;
}
5.2.3 Early Demux优化

tcp_v4_early_demux() (net/ipv4/tcp_ipv4.c:1545):

c 复制代码
void tcp_v4_early_demux(struct sk_buff *skb)
{
    const struct iphdr *iph;
    const struct tcphdr *th;
    struct sock *sk;

    if (skb->pkt_type != PACKET_HOST)
        return;

    if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)))
        return;

    iph = ip_hdr(skb);
    th = tcp_hdr(skb);

    if (th->doff < sizeof(struct tcphdr) / 4)
        return;

    // 查找socket
    sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
                                   iph->saddr, th->source,
                                   iph->daddr, ntohs(th->dest),
                                   skb->skb_iif);
    if (sk) {
        skb->sk = sk;  // 关联socket
        skb->destructor = sock_edemux;

        // 缓存目的路由
        if (sk_fullsock(sk)) {
            struct dst_entry *dst = READ_ONCE(sk->sk_rx_dst);

            if (dst)
                dst = dst_check(dst, 0);
            if (dst &&
                inet_sk(sk)->rx_dst_ifindex == skb->skb_iif)
                skb_dst_set_noref(skb, dst);
        }
    }
}

优势:

  • 提前进行socket查找,后续处理直接使用
  • 缓存路由信息,避免重复查找
  • 显著提升已建立连接的处理性能
5.2.4 本地交付

ip_local_deliver() (net/ipv4/ip_input.c:246):

c 复制代码
int ip_local_deliver(struct sk_buff *skb)
{
    /*
     * IP分片重组
     * 如果是分片包,先重组
     */
    if (ip_is_fragment(ip_hdr(skb))) {
        if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
            return 0;
    }

    // Netfilter钩子: LOCAL_IN
    return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
                   dev_net(skb->dev), NULL, skb, skb->dev, NULL,
                   ip_local_deliver_finish);
}

ip_local_deliver_finish() (net/ipv4/ip_input.c:191):

c 复制代码
static int ip_local_deliver_finish(struct sock *sk, struct sk_buff *skb)
{
    struct net *net = dev_net(skb->dev);

    __skb_pull(skb, skb_network_header_len(skb));  // 剥离IP头

    rcu_read_lock();
    {
        int protocol = ip_hdr(skb)->protocol;
        const struct net_protocol *ipprot;
        int raw;

    resubmit:
        raw = raw_local_deliver(skb, protocol);  // RAW socket处理

        // 查找传输层协议处理函数
        ipprot = rcu_dereference(inet_protos[protocol]);
        if (ipprot) {
            int ret;

            if (!ipprot->no_policy) {
                if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                    kfree_skb(skb);
                    goto out;
                }
                nf_reset(skb);
            }

            // 调用传输层处理函数
            // 对于TCP(protocol=6): tcp_v4_rcv
            // 对于UDP(protocol=17): udp_rcv
            // 对于ICMP(protocol=1): icmp_rcv
            ret = ipprot->handler(skb);
            if (ret < 0) {
                protocol = -ret;
                goto resubmit;
            }
            IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
        } else {
            if (!raw) {
                if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
                    IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);
                    icmp_send(skb, ICMP_DEST_UNREACH,
                             ICMP_PROT_UNREACH, 0);
                }
                kfree_skb(skb);
            } else {
                IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
                consume_skb(skb);
            }
        }
    }
out:
    rcu_read_unlock();

    return 0;
}
5.2.5 传输层协议分发

net_protocol结构 (include/net/protocol.h:41):

c 复制代码
struct net_protocol {
    int         (*early_demux)(struct sk_buff *skb);
    int         (*handler)(struct sk_buff *skb);
    void        (*err_handler)(struct sk_buff *skb, u32 info);
    unsigned int    no_policy:1,
                    netns_ok:1;
};

TCP协议注册 (net/ipv4/af_inet.c:1557):

c 复制代码
static const struct net_protocol tcp_protocol = {
    .early_demux    = tcp_v4_early_demux,
    .handler        = tcp_v4_rcv,
    .err_handler    = tcp_v4_err,
    .no_policy      = 1,
    .netns_ok       = 1,
};

// 在inet_init()中注册:
if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
    pr_crit("%s: Cannot add TCP protocol\n", __func__);

协议数组:

c 复制代码
const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
// MAX_INET_PROTOS = 256
// 索引是IP协议号:
//   IPPROTO_TCP = 6
//   IPPROTO_UDP = 17
//   IPPROTO_ICMP = 1

5.3 传输层(L4) - TCP处理

5.3.1 TCP接收入口

tcp_v4_rcv() (net/ipv4/tcp_ipv4.c:1657):

c 复制代码
int tcp_v4_rcv(struct sk_buff *skb)
{
    const struct iphdr *iph;
    const struct tcphdr *th;
    struct sock *sk;
    int ret;
    struct net *net = dev_net(skb->dev);

    // 包类型检查
    if (skb->pkt_type != PACKET_HOST)
        goto discard_it;

    /* 统计入站TCP段 */
    TCP_INC_STATS_BH(net, TCP_MIB_INSEGS);

    // 确保包含TCP头
    if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
        goto discard_it;

    th = tcp_hdr(skb);

    // 验证TCP头长度
    if (th->doff < sizeof(struct tcphdr) / 4)
        goto bad_packet;
    if (!pskb_may_pull(skb, th->doff * 4))
        goto discard_it;

    /*
     * 验证TCP校验和
     * 使用伪头部(IP地址+协议号+TCP长度)
     */
    if (skb_checksum_init(skb, IPPROTO_TCP, inet_compute_pseudo))
        goto csum_error;

    th = tcp_hdr(skb);
    iph = ip_hdr(skb);

    /* 初始化TCP控制块 */
    TCP_SKB_CB(skb)->seq = ntohl(th->seq);
    TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
                                skb->len - th->doff * 4);
    TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
    TCP_SKB_CB(skb)->tcp_flags = tcp_flag_byte(th);
    TCP_SKB_CB(skb)->tcp_tw_isn = 0;
    TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph);
    TCP_SKB_CB(skb)->sacked  = 0;

    /*
     * Socket查找
     * 使用四元组(saddr, sport, daddr, dport)
     */
    sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
    if (!sk)
        goto no_tcp_socket;

process:
    // TIME_WAIT状态特殊处理
    if (sk->sk_state == TCP_TIME_WAIT)
        goto do_time_wait;

    // NEW_SYN_RECV状态(SYN_RECV cookie)
    if (sk->sk_state == TCP_NEW_SYN_RECV) {
        struct request_sock *req = inet_reqsk(sk);
        struct sock *nsk;

        sk = req->rsk_listener;
        tcp_v4_fill_cb(skb, iph, th);
        if (tcp_v4_inbound_md5_hash(sk, skb))
            goto discard_and_relse;
        nsk = tcp_check_req(sk, skb, req, false);
        if (!nsk) {
            reqsk_put(req);
            goto discard_it;
        }
        if (nsk == sk) {
            sock_hold(sk);
            reqsk_put(req);
            tcp_v4_restore_cb(skb);
        } else if (tcp_child_process(sk, nsk, skb)) {
            tcp_v4_send_reset(nsk, skb);
            goto discard_it;
        } else {
            sock_put(sk);
            return 0;
        }
    }

    // 安全策略检查
    if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
        goto discard_and_relse;

    tcp_v4_fill_cb(skb, iph, th);

    // MD5签名检查
    if (tcp_v4_inbound_md5_hash(sk, skb))
        goto discard_and_relse;

    nf_reset(skb);

    // Netfilter处理后可能改变skb
    if (sk_filter(sk, skb))
        goto discard_and_relse;

    skb->dev = NULL;

    // 锁定socket
    bh_lock_sock_nested(sk);
    tcp_sk(sk)->segs_in += max_t(u16, 1, skb_shinfo(skb)->gso_segs);
    ret = 0;

    // Socket未被用户进程锁定
    if (!sock_owned_by_user(sk)) {
        if (!tcp_prequeue(sk, skb))
            ret = tcp_v4_do_rcv(sk, skb);
    } else if (unlikely(sk_add_backlog(sk, skb,
                                      sk->sk_rcvbuf + sk->sk_sndbuf))) {
        // 加入backlog队列
        bh_unlock_sock(sk);
        NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
        goto discard_and_relse;
    }
    bh_unlock_sock(sk);

    sock_put(sk);

    return ret;

no_tcp_socket:
    // 未找到socket
    if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
        goto discard_it;

    tcp_v4_fill_cb(skb, iph, th);

    if (skb->len < (th->doff << 2) || tcp_checksum_complete(skb)) {
csum_error:
        TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
bad_packet:
        TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
    } else {
        // 发送RST
        tcp_v4_send_reset(NULL, skb);
    }

discard_it:
    kfree_skb(skb);
    return 0;

discard_and_relse:
    sock_put(sk);
    goto discard_it;

do_time_wait:
    // TIME_WAIT状态处理
    if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
        inet_twsk_put(inet_twsk(sk));
        goto discard_it;
    }

    tcp_v4_fill_cb(skb, iph, th);

    if (tcp_checksum_complete(skb)) {
        inet_twsk_put(inet_twsk(sk));
        goto csum_error;
    }
    switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
    case TCP_TW_SYN: {
        struct sock *sk2 = inet_lookup_listener(dev_net(skb->dev),
                                               &tcp_hashinfo, skb,
                                               ip_hdrlen(skb) +
                                               th->source, iph->saddr,
                                               th->source, iph->daddr,
                                               ntohs(th->dest),
                                               inet_iif(skb));
        if (sk2) {
            inet_twsk_deschedule(inet_twsk(sk));
            inet_twsk_put(inet_twsk(sk));
            sk = sk2;
            tcp_v4_restore_cb(skb);
            goto process;
        }
        /* Fall through to ACK */
    }
    case TCP_TW_ACK:
        tcp_v4_timewait_ack(sk, skb);
        break;
    case TCP_TW_RST:
        tcp_v4_send_reset(sk, skb);
        inet_twsk_deschedule(inet_twsk(sk));
        inet_twsk_put(inet_twsk(sk));
        goto discard_it;
    case TCP_TW_SUCCESS:;
    }
    goto discard_it;
}
5.3.2 TCP控制块

TCP_SKB_CB宏 (include/net/tcp.h):

c 复制代码
#define TCP_SKB_CB(__skb)   ((struct tcp_skb_cb *)&((__skb)->cb[0]))

struct tcp_skb_cb {
    __u32       seq;            // 起始序号
    __u32       end_seq;        // 结束序号
    union {
        __u32   tcp_tw_isn;     // TIME_WAIT ISN
        struct {
            u16 tcp_gso_segs;
            u16 tcp_gso_size;
        };
    };
    __u8        tcp_flags;      // TCP标志位
    __u8        sacked;         // SACK标志
    __u8        ip_dsfield;     // IP DS字段
    __u8        txstamp_ack:1,  // 需要TX时间戳
                eor:1,          // 记录结束
                unused:6;
    __u32       ack_seq;        // ACK序号
    union {
        struct {
            __u32 is_app_limited:1;
            __u32 unused:31;
        } tx;
        struct inet_skb_parm h4;
    } header;
};

5.3.3 Socket查找

__inet_lookup_skb() (include/net/inet_hashtables.h):

c 复制代码
static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,
                                            struct sk_buff *skb,
                                            const __be16 sport,
                                            const __be16 dport)
{
    struct sock *sk = skb_steal_sock(skb);
    const struct iphdr *iph = ip_hdr(skb);

    if (sk)
        return sk;
    else
        return __inet_lookup(dev_net(skb_dst(skb)->dev), hashinfo,
                           iph->saddr, sport,
                           iph->daddr, dport,
                           inet_iif(skb));
}

struct sock *__inet_lookup(struct net *net, struct inet_hashinfo *hashinfo,
                          const __be32 saddr, const __be16 sport,
                          const __be32 daddr, const __be16 dport,
                          const int dif)
{
    struct sock *sk;

    // 首先查找已建立连接
    sk = __inet_lookup_established(net, hashinfo,
                                   saddr, sport, daddr, dport, dif);
    if (sk)
        return sk;

    // 未找到,查找监听socket
    return __inet_lookup_listener(net, hashinfo, skb, doff,
                                  saddr, sport, daddr, dport, dif);
}

TCP哈希表 (net/ipv4/tcp_ipv4.c):

c 复制代码
struct inet_hashinfo tcp_hashinfo;

// 哈希表包含:
// 1. ehash: 已建立连接哈希表
// 2. bhash: 绑定端口哈希表
// 3. listening_hash: 监听socket哈希表
5.3.4 TCP处理分发

tcp_v4_do_rcv() (net/ipv4/tcp_ipv4.c:1483):

c 复制代码
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
    struct sock *rsk;

    // 快速路径:已建立连接
    if (sk->sk_state == TCP_ESTABLISHED) {
        struct dst_entry *dst = sk->sk_rx_dst;

        sock_rps_save_rxhash(sk, skb);
        sk_mark_napi_id(sk, skb);

        // 验证目的路由缓存
        if (dst) {
            if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
                dst->ops->check(dst, 0) == NULL) {
                dst_release(dst);
                sk->sk_rx_dst = NULL;
            }
        }

        // 处理已建立连接的数据包
        tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len);
        return 0;
    }

    // 慢速路径:其他状态
    if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))
        goto csum_err;

    if (sk->sk_state == TCP_LISTEN) {
        // 处理SYN包,连接建立
        struct sock *nsk = tcp_v4_hnd_req(sk, skb);
        if (!nsk)
            goto discard;

        if (nsk != sk) {
            sock_rps_save_rxhash(nsk, skb);
            sk_mark_napi_id(nsk, skb);
            if (tcp_child_process(sk, nsk, skb)) {
                rsk = nsk;
                goto reset;
            }
            return 0;
        }
    } else
        sock_rps_save_rxhash(sk, skb);

    // 状态机处理(SYN_SENT, FIN_WAIT等)
    if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {
        rsk = sk;
        goto reset;
    }
    return 0;

reset:
    tcp_v4_send_reset(rsk, skb);
discard:
    kfree_skb(skb);
    return 0;

csum_err:
    TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_CSUMERRORS);
    TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
    goto discard;
}
5.3.5 已建立连接处理(快速路径)

tcp_rcv_established() (net/ipv4/tcp_input.c:5287):

c 复制代码
void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
                        const struct tcphdr *th, unsigned int len)
{
    struct tcp_sock *tp = tcp_sk(sk);

    if (unlikely(!sk->sk_rx_dst))
        inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);

    /*
     * 头部预测(Header Prediction)
     * 这是TCP最重要的优化!
     * 条件:
     * 1. 预期的标志位
     * 2. 预期的序号
     * 3. 预期的ACK
     */
    if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
        TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
        !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {

        int tcp_header_len = tp->tcp_header_len;

        /* 检查时间戳选项 */
        if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) {
            /* 没有MD5签名 */
            __be32 *ptr = (__be32 *)(th + 1);

            /* 快速解析时间戳 */
            if (*ptr == htonl((TCPOPT_NOP << 24) |
                             (TCPOPT_NOP << 16) |
                             (TCPOPT_TIMESTAMP << 8) |
                             TCPOLEN_TIMESTAMP)) {
                tp->rx_opt.saw_tstamp = 1;
                ++ptr;
                tp->rx_opt.rcv_tsval = ntohl(*ptr);
                ++ptr;
                tp->rx_opt.rcv_tsecr = ntohl(*ptr);

                /* 检查时间戳 */
                if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) < 0)
                    goto slow_path;

                /* 更新ts_recent */
                tp->rx_opt.ts_recent = tp->rx_opt.rcv_tsval;
                tp->rx_opt.ts_recent_stamp = get_seconds();
            } else
                goto slow_path;
        }

        if (len <= tcp_header_len) {
            /* 纯ACK包 */
            if (len == tcp_header_len) {
                /* 批量接收优化 */
                tcp_ack(sk, skb, 0);
                __kfree_skb(skb);
                tcp_data_snd_check(sk);
                return;
            }
        } else {
            /* 数据包 */
            int eaten = 0;
            bool fragstolen = false;

            /*
             * ucopy优化:直接拷贝到用户空间
             * 条件:
             * 1. 用户进程正在等待
             * 2. 数据是顺序的
             * 3. 有足够的用户空间缓冲区
             * 4. socket被用户进程锁定
             */
            if (tp->ucopy.task == current &&
                tp->copied_seq == tp->rcv_nxt &&
                len - tcp_header_len <= tp->ucopy.len &&
                sock_owned_by_user(sk)) {
                __set_current_state(TASK_RUNNING);

                // 直接拷贝到用户空间!
                if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) {
                    /*
                     * 成功!数据直接从软中断上下文
                     * 拷贝到了用户空间,无需入队
                     */
                    tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
                    NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITSTOUSER);
                    eaten = 1;
                }
            }

            if (!eaten) {
                /*
                 * 常规路径:加入接收队列
                 */
                if (tcp_checksum_complete_user(sk, skb))
                    goto csum_error;

                if ((int)skb->truesize > sk->sk_forward_alloc)
                    goto step5;

                /* 预测成功:快速入队 */
                eaten = tcp_queue_rcv(sk, skb, tcp_header_len, &fragstolen);

                tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPHITS);
            }

            tcp_event_data_recv(sk, skb);

            /* 处理ACK */
            if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {
                /* Well, only one small jumplet in fast path... */
                tcp_ack(sk, skb, FLAG_DATA);
                tcp_data_snd_check(sk);
                if (!inet_csk_ack_scheduled(sk))
                    goto no_ack;
            }

            __tcp_ack_snd_check(sk, 0);
no_ack:
            if (eaten)
                kfree_skb_partial(skb, fragstolen);

            /* 唤醒等待的用户进程 */
            sk->sk_data_ready(sk, 0);
            return;
        }
    }

slow_path:
    /*
     * 慢速路径
     * 处理:乱序、重传、异常标志等
     */
    if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
        goto csum_error;

    /* 验证包的有效性 */
    if (!tcp_validate_incoming(sk, skb, th, 1))
        return;

step5:
    /* 处理ACK */
    if (tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) < 0)
        goto discard;

    tcp_rcv_rtt_measure_ts(tp, skb);

    /* 处理紧急数据 */
    tcp_urg(sk, skb, th);

    /* 处理数据 */
    tcp_data_queue(sk, skb);

    tcp_data_snd_check(sk);
    tcp_ack_snd_check(sk);
    return;

csum_error:
    TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_CSUMERRORS);
    TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);

discard:
    __kfree_skb(skb);
}

关键优化点:

  1. Header Prediction: 预测数据包特征,走快速路径
  2. ucopy优化: 直接拷贝到用户空间,避免入队
  3. GRO批处理: 多个小包合并处理
5.3.6 TCP数据入队

tcp_queue_rcv() (net/ipv4/tcp_input.c:4455):

c 复制代码
static int tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int hdrlen,
                         bool *fragstolen)
{
    int eaten;
    struct sk_buff *tail = skb_peek_tail(&sk->sk_receive_queue);

    __skb_pull(skb, hdrlen);  // 剥离TCP头

    eaten = (tail &&
            tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0;

    tcp_sk(sk)->rcv_nxt = TCP_SKB_CB(skb)->end_seq;

    if (!eaten) {
        // 加入接收队列
        __skb_queue_tail(&sk->sk_receive_queue, skb);
        skb_set_owner_r(skb, sk);
    }
    return eaten;
}

tcp_try_coalesce(): 尝试将新包合并到队尾的skb中,减少内存使用。

5.3.7 数据队列处理(慢速路径)

tcp_data_queue() (net/ipv4/tcp_input.c:4530):

c 复制代码
static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
{
    struct tcp_sock *tp = tcp_sk(sk);
    bool fragstolen = false;
    int eaten = -1;

    // 空数据包
    if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) {
        __kfree_skb(skb);
        return;
    }

    skb_dst_drop(skb);
    __skb_pull(skb, tcp_hdr(skb)->doff * 4);  // 剥离TCP头

    TCP_ECN_accept_cwr(tp, skb);

    tp->rx_opt.dsack = 0;

    /*
     * 队列管理:
     * 1. 顺序包 → sk_receive_queue
     * 2. 乱序包 → out_of_order_queue (红黑树)
     */

    if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
        /* 顺序包!*/
        if (tcp_receive_window(tp) == 0)
            goto out_of_window;

        /* ucopy优化:直接拷贝到用户空间 */
        if (tp->ucopy.task == current &&
            tp->copied_seq == tp->rcv_nxt && tp->ucopy.len &&
            sock_owned_by_user(sk) && !tp->urg_data) {
            int chunk = min_t(unsigned int, skb->len, tp->ucopy.len);

            __set_current_state(TASK_RUNNING);

            local_bh_enable();
            if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) {
                tp->ucopy.len -= chunk;
                tp->copied_seq += chunk;
                eaten = (chunk == skb->len);
                tcp_rcv_space_adjust(sk);
            }
            local_bh_disable();
        }

        if (eaten <= 0) {
queue_and_out:
            // 加入接收队列
            eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
        }

        tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);

        if (skb->len)
            tcp_event_data_recv(sk, skb);

        if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
            tcp_fin(sk);

        // 处理乱序队列中变成顺序的包
        if (!RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
            tcp_ofo_queue(sk);

            /* RFC2581: 每收到两个顺序包,发送一个ACK */
            if (tp->rcv_nxt == tp->rcv_wup)
                inet_csk(sk)->icsk_ack.pingpong = 0;
        }

        if (tp->rx_opt.num_sacks)
            tcp_sack_remove(tp);

        tcp_fast_path_check(sk);

        if (eaten > 0)
            kfree_skb_partial(skb, fragstolen);

        // 唤醒等待的进程
        if (!sock_flag(sk, SOCK_DEAD))
            sk->sk_data_ready(sk, 0);
        return;
    }

    // 乱序包处理
    if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
        /* 旧数据(可能是重传),丢弃 */
        __kfree_skb(skb);
        return;
    }

    /* 乱序数据 */
    tcp_data_queue_ofo(sk, skb);
}
5.3.8 乱序队列处理

tcp_data_queue_ofo() (net/ipv4/tcp_input.c:4273):

c 复制代码
static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct rb_node **p, *parent;
    struct sk_buff *skb1;
    u32 seq, end_seq;

    tcp_ecn_check_ce(tp, skb);

    // 检查接收窗口
    if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) {
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFODROP);
        __kfree_skb(skb);
        return;
    }

    /* 禁用头部预测 */
    tp->pred_flags = 0;
    inet_csk_schedule_ack(sk);

    NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFOQUEUE);
    seq = TCP_SKB_CB(skb)->seq;
    end_seq = TCP_SKB_CB(skb)->end_seq;

    /*
     * 红黑树插入
     * 按序号排序
     */
    p = &tp->out_of_order_queue.rb_node;
    if (RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
        // 第一个乱序包
        rb_link_node(&skb->rbnode, NULL, p);
        rb_insert_color(&skb->rbnode, &tp->out_of_order_queue);
        tp->ooo_last_skb = skb;
        goto end;
    }

    /* 优化:大多数情况新包在队尾 */
    if (skb1 == tp->ooo_last_skb) {
        parent = &skb1->rbnode;
        p = &parent->rb_right;
        goto insert;
    }

    /* 在红黑树中查找插入位置 */
    while (*p) {
        parent = *p;
        skb1 = rb_to_skb(parent);

        if (before(seq, TCP_SKB_CB(skb1)->seq)) {
            p = &parent->rb_left;
            continue;
        }
        if (before(seq, TCP_SKB_CB(skb1)->end_seq)) {
            /* 重叠,需要合并或丢弃 */
            if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {
                /* 完全重复,丢弃 */
                __kfree_skb(skb);
                return;
            }
            /* 部分重叠,修剪 */
            if (before(seq, TCP_SKB_CB(skb1)->seq)) {
                /* 前部重叠 */
                tcp_collapse_one(sk, skb1);
            }
        }
        p = &parent->rb_right;
    }

insert:
    rb_link_node(&skb->rbnode, parent, p);
    rb_insert_color(&skb->rbnode, &tp->out_of_order_queue);

end:
    skb_set_owner_r(skb, sk);
}

tcp_ofo_queue(): 检查乱序队列,将变成顺序的包移到接收队列

c 复制代码
static void tcp_ofo_queue(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);
    __u32 dsack_high = tp->rcv_nxt;
    struct sk_buff *skb, *tail;
    struct rb_node *p;

    p = rb_first(&tp->out_of_order_queue);
    while (p) {
        skb = rb_to_skb(p);
        if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt))
            break;

        // 这个包现在是顺序的了
        p = rb_next(p);
        rb_erase(&skb->rbnode, &tp->out_of_order_queue);

        if (unlikely(!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt))) {
            // 已经收到过,丢弃
            __kfree_skb(skb);
            continue;
        }

        // 移到接收队列
        tail = skb_peek_tail(&sk->sk_receive_queue);
        eaten = tail && tcp_try_coalesce(sk, tail, skb, &fragstolen);
        tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);

        if (!eaten)
            __skb_queue_tail(&sk->sk_receive_queue, skb);

        if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
            tcp_fin(sk);
        if (eaten)
            kfree_skb_partial(skb, fragstolen);
    }
}
5.3.9 唤醒等待进程

sk->sk_data_ready() 指向 sock_def_readable() (net/core/sock.c:2363):

c 复制代码
static void sock_def_readable(struct sock *sk, int len)
{
    struct socket_wq *wq;

    rcu_read_lock();
    wq = rcu_dereference(sk->sk_wq);

    // 检查是否有进程在等待
    if (wq_has_sleeper(wq))
        // 唤醒所有等待的进程
        wake_up_interruptible_sync_poll(&wq->wait,
                                        POLLIN | POLLPRI |
                                        POLLRDNORM | POLLRDBAND);

    // 异步通知(SIGIO信号)
    sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);

    rcu_read_unlock();
}

wake_up_interruptible_sync_poll() : 唤醒在sk->sk_wq->wait等待队列上的进程。


6. Socket层与系统调用

6.1 核心数据结构

6.1.1 struct socket (通用Socket)

文件 : include/linux/net.h:107

c 复制代码
struct socket {
    socket_state        state;      // Socket状态

    short               type;       // Socket类型
                                    // SOCK_STREAM (TCP)
                                    // SOCK_DGRAM (UDP)
                                    // SOCK_RAW

    unsigned long       flags;      // Socket标志

    struct socket_wq __rcu *wq;     // 等待队列

    struct file         *file;      // 关联的文件对象

    struct sock         *sk;        // 协议相关的sock

    const struct proto_ops *ops;    // 协议操作函数集
                                    // inet_stream_ops (TCP)
                                    // inet_dgram_ops (UDP)
};

Socket状态:

c 复制代码
typedef enum {
    SS_FREE = 0,            // 未分配
    SS_UNCONNECTED,         // 未连接
    SS_CONNECTING,          // 正在连接
    SS_CONNECTED,           // 已连接
    SS_DISCONNECTING        // 正在断开
} socket_state;

socket_wq (等待队列):

c 复制代码
struct socket_wq {
    wait_queue_head_t   wait;       // 等待队列头
    struct fasync_struct *fasync_list;  // 异步通知
    struct rcu_head     rcu;
};
6.1.2 struct sock (协议相关Socket)

文件 : include/net/sock.h:303

c 复制代码
struct sock {
    struct sock_common  __sk_common;  // 公共部分(hash信息等)

    socket_lock_t       sk_lock;      // Socket锁

    /*
     * 接收队列 - 关键!
     */
    struct sk_buff_head sk_receive_queue;  // 接收队列

    /*
     * 后备队列
     * 当socket被用户进程锁定时,新到的包放这里
     */
    struct {
        atomic_t    rmem_alloc;       // 已分配的接收内存
        int         len;              // 队列长度
        struct sk_buff  *head;        // 队列头
        struct sk_buff  *tail;        // 队列尾
    } sk_backlog;

    int                 sk_rcvbuf;    // 接收缓冲区大小限制

    atomic_t            sk_rmem_alloc; // 已分配接收内存
    atomic_t            sk_wmem_alloc; // 已分配发送内存

    /*
     * 回调函数
     */
    void                (*sk_data_ready)(struct sock *sk, int bytes);
    void                (*sk_write_space)(struct sock *sk);
    void                (*sk_error_report)(struct sock *sk);

    /*
     * 协议操作
     */
    struct proto        *sk_prot;     // 协议操作集
                                      // tcp_prot (TCP)
                                      // udp_prot (UDP)

    /*
     * 网络命名空间
     */
    struct net          *sk_net;

    /*
     * 状态和类型
     */
    unsigned char       sk_protocol;  // 协议号 (IPPROTO_TCP=6)
    unsigned short      sk_type;      // Socket类型 (SOCK_STREAM)
    int                 sk_state;     // TCP状态

    /*
     * 地址信息
     */
    __be32              sk_daddr;     // 目的IP
    __be32              sk_rcv_saddr; // 本地IP
    __be16              sk_dport;     // 目的端口
    __u16               sk_num;       // 本地端口

    /*
     * 定时器
     */
    struct timer_list   sk_timer;     // Socket定时器

    /*
     * 错误队列
     */
    struct sk_buff_head sk_error_queue;

    /*
     * 用户数据
     */
    void                *sk_user_data;
};

sock_common (hash和地址信息):

c 复制代码
struct sock_common {
    union {
        __addrpair      skc_addrpair;   // 本地+远程地址
        struct {
            __be32      skc_daddr;      // 目的地址
            __be32      skc_rcv_saddr;  // 本地地址
        };
    };
    union {
        __portpair      skc_portpair;   // 本地+远程端口
        struct {
            __be16      skc_dport;      // 目的端口
            __u16       skc_num;        // 本地端口
        };
    };

    unsigned short      skc_family;     // 地址族 (AF_INET)
    volatile unsigned char  skc_state;  // 连接状态

    struct hlist_node   skc_node;       // hash链表节点
    struct hlist_nulls_node skc_nulls_node;

    int                 skc_bound_dev_if; // 绑定设备索引

    struct net          *skc_net;       // 网络命名空间
};
6.1.3 struct tcp_sock (TCP专用)

文件 : include/linux/tcp.h:138

c 复制代码
struct tcp_sock {
    /* inet_connection_sock必须是第一个成员 */
    struct inet_connection_sock inet_conn;

    /*
     * 接收相关
     */
    u32     rcv_nxt;        // 下一个期望接收的序号
    u32     copied_seq;     // 已拷贝到用户空间的序号
    u32     rcv_wup;        // 窗口更新发送时的rcv_nxt
    u32     snd_nxt;        // 下一个要发送的序号

    u32     snd_una;        // 最老的未确认序号
    u32     snd_sml;        // 最后一次发送的序号
    u32     rcv_tstamp;     // 最后接收时间
    u32     lsndtime;       // 最后发送数据时间

    /*
     * ucopy机制 - 直接拷贝到用户空间优化
     */
    struct {
        struct sk_buff_head  prequeue;   // 预队列
        struct task_struct   *task;      // 拥有socket的进程
        struct iovec         *iov;       // 用户空间iovec
        int                  memory;     // 预队列内存
        int                  len;        // 剩余长度
    } ucopy;

    /*
     * 窗口管理
     */
    u32     snd_wnd;        // 发送窗口
    u32     max_window;     // 最大接收窗口
    u32     mss_cache;      // 当前MSS

    u32     window_clamp;   // 最大接收窗口
    u32     rcv_ssthresh;   // 接收慢启动阈值

    /*
     * 拥塞控制
     */
    u32     snd_ssthresh;   // 慢启动阈值
    u32     snd_cwnd;       // 拥塞窗口
    u32     snd_cwnd_cnt;   // 拥塞窗口计数
    u32     snd_cwnd_clamp; // 拥塞窗口最大值

    /*
     * RTT测量
     */
    u32     srtt_us;        // 平滑RTT (us)
    u32     mdev_us;        // RTT平均偏差
    u32     mdev_max_us;    // RTT最大偏差
    u32     rttvar_us;      // RTT方差
    u32     rtt_seq;        // RTT测量序号

    /*
     * 重传
     */
    u32     packets_out;    // 在网络中的包数
    u32     retrans_out;    // 重传中的包数
    u16     urg_data;       // 紧急数据
    u8      ecn_flags;      // ECN状态
    u8      keepalive_probes; // keepalive探测数
    u32     reordering;     // 重排序度量
    u32     snd_up;         // 紧急指针

    /*
     * SACK
     */
    u8      rx_opt.num_sacks;     // SACK块数量
    struct tcp_sack_block duplicate_sack[1];
    struct tcp_sack_block selective_acks[4];

    /*
     * 乱序队列(红黑树)
     */
    struct rb_root  out_of_order_queue;
    struct sk_buff  *ooo_last_skb;  // 乱序队列尾缓存

    /*
     * 发送队列
     */
    struct sk_buff_head out_of_order_queue_old; // 旧版本兼容

    /*
     * MD5签名
     */
    #ifdef CONFIG_TCP_MD5SIG
    struct tcp_md5sig_info __rcu *md5sig_info;
    #endif

    /*
     * 时间戳选项
     */
    struct {
        u8  saw_tstamp:1;
        u8  tstamp_ok:1;
        u8  dsack:1;
        u8  wscale_ok:1;
        u8  sack_ok:4;
        u8  snd_wscale:4;
        u8  rcv_wscale:4;
        u32 rcv_tsval;
        u32 rcv_tsecr;
        u32 ts_recent;
        u32 ts_recent_stamp;
    } rx_opt;
};

6.2 系统调用层

6.2.1 recv/recvfrom系统调用

SYSCALL_DEFINE6(recvfrom) (net/socket.c:1864):

c 复制代码
SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
                unsigned int, flags, struct sockaddr __user *, addr,
                int __user *, addr_len)
{
    struct socket *sock;
    struct iovec iov;
    struct msghdr msg;
    struct sockaddr_storage address;
    int err, err2;
    int fput_needed;

    // 通过文件描述符查找socket
    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (!sock)
        goto out;

    // 构造消息结构
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_iovlen = 1;
    msg.msg_iov = &iov;
    iov.iov_len = size;
    iov.iov_base = ubuf;
    msg.msg_name = (struct sockaddr *)&address;
    msg.msg_namelen = sizeof(address);
    msg.msg_flags = 0;

    if (sock->file->f_flags & O_NONBLOCK)
        flags |= MSG_DONTWAIT;

    // 调用socket接收函数
    err = sock_recvmsg(sock, &msg, size, flags);

    // 拷贝地址信息到用户空间
    if (err >= 0 && addr != NULL) {
        err2 = move_addr_to_user(&address,
                                 msg.msg_namelen, addr, addr_len);
        if (err2 < 0)
            err = err2;
    }

    fput_light(sock->file, fput_needed);
out:
    return err;
}

SYSCALL_DEFINE3(recvmsg):

c 复制代码
SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
                unsigned int, flags)
{
    int fput_needed, err;
    struct msghdr msg_sys;
    struct socket *sock;

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (!sock)
        goto out;

    err = ___sys_recvmsg(sock, msg, &msg_sys, flags, 0);

    fput_light(sock->file, fput_needed);
out:
    return err;
}
6.2.2 sock_recvmsg流程

sock_recvmsg() (net/socket.c:828):

c 复制代码
int sock_recvmsg(struct socket *sock, struct msghdr *msg,
                size_t size, int flags)
{
    int err = security_socket_recvmsg(sock, msg, size, flags);

    return err ?: __sock_recvmsg_nosec(sock, msg, size, flags);
}

static inline int __sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg,
                                      size_t size, int flags)
{
    return sock_recvmsg_nosec(sock, msg, size, flags);
}

static inline int sock_recvmsg_nosec(struct socket *sock, struct msghdr *msg,
                                    size_t size, int flags)
{
    // 调用协议操作函数
    return sock->ops->recvmsg(sock, msg, size, flags);
}

6.2.3 INET层接收

inet_recvmsg() (net/ipv4/af_inet.c:762):

c 复制代码
int inet_recvmsg(struct kiocb *iocb, struct socket *sock,
                 struct msghdr *msg, size_t size, int flags)
{
    struct sock *sk = sock->sk;
    int addr_len = 0;
    int err;

    sock_rps_record_flow(sk);  // 记录RPS流

    // 调用传输层协议的recvmsg
    // 对于TCP: tcp_prot->recvmsg = tcp_recvmsg
    err = sk->sk_prot->recvmsg(iocb, sk, msg, size,
                               flags & MSG_DONTWAIT,
                               flags & ~MSG_DONTWAIT, &addr_len);
    if (err >= 0)
        msg->msg_namelen = addr_len;
    return err;
}

proto_ops绑定 (net/ipv4/af_inet.c:904):

c 复制代码
const struct proto_ops inet_stream_ops = {
    .family        = PF_INET,
    .owner         = THIS_MODULE,
    .release       = inet_release,
    .bind          = inet_bind,
    .connect       = inet_stream_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = inet_accept,
    .getname       = inet_getname,
    .poll          = tcp_poll,
    .ioctl         = inet_ioctl,
    .listen        = inet_listen,
    .shutdown      = inet_shutdown,
    .setsockopt    = sock_common_setsockopt,
    .getsockopt    = sock_common_getsockopt,
    .sendmsg       = inet_sendmsg,
    .recvmsg       = inet_recvmsg,  // ← 这里
    .mmap          = sock_no_mmap,
    .sendpage      = inet_sendpage,
    .splice_read   = tcp_splice_read,
    #ifdef CONFIG_COMPAT
    .compat_setsockopt = compat_sock_common_setsockopt,
    .compat_getsockopt = compat_sock_common_getsockopt,
    .compat_ioctl  = inet_compat_ioctl,
    #endif
};
EXPORT_SYMBOL(inet_stream_ops);

proto绑定 (net/ipv4/tcp_ipv4.c:2454):

c 复制代码
struct proto tcp_prot = {
    .name              = "TCP",
    .owner             = THIS_MODULE,
    .close             = tcp_close,
    .connect           = tcp_v4_connect,
    .disconnect        = tcp_disconnect,
    .accept            = inet_csk_accept,
    .ioctl             = tcp_ioctl,
    .init              = tcp_v4_init_sock,
    .destroy           = tcp_v4_destroy_sock,
    .shutdown          = tcp_shutdown,
    .setsockopt        = tcp_setsockopt,
    .getsockopt        = tcp_getsockopt,
    .recvmsg           = tcp_recvmsg,  // ← 这里
    .sendmsg           = tcp_sendmsg,
    .sendpage          = tcp_sendpage,
    .backlog_rcv       = tcp_v4_do_rcv,
    .release_cb        = tcp_release_cb,
    .hash              = inet_hash,
    .unhash            = inet_unhash,
    .get_port          = inet_csk_get_port,
    .enter_memory_pressure = tcp_enter_memory_pressure,
    .stream_memory_free = tcp_stream_memory_free,
    .sockets_allocated = &tcp_sockets_allocated,
    .orphan_count      = &tcp_orphan_count,
    .memory_allocated  = &tcp_memory_allocated,
    .memory_pressure   = &tcp_memory_pressure,
    .sysctl_mem        = sysctl_tcp_mem,
    .sysctl_wmem       = sysctl_tcp_wmem,
    .sysctl_rmem       = sysctl_tcp_rmem,
    .max_header        = MAX_TCP_HEADER,
    .obj_size          = sizeof(struct tcp_sock),
    .slab_flags        = SLAB_DESTROY_BY_RCU,
    .twsk_prot         = &tcp_timewait_sock_ops,
    .rsk_prot          = &tcp_request_sock_ops,
    .h.hashinfo        = &tcp_hashinfo,
    .no_autobind       = true,
    #ifdef CONFIG_COMPAT
    .compat_setsockopt = compat_tcp_setsockopt,
    .compat_getsockopt = compat_tcp_getsockopt,
    #endif
    #ifdef CONFIG_MEMCG_KMEM
    .init_cgroup       = tcp_init_cgroup,
    .destroy_cgroup    = tcp_destroy_cgroup,
    .proto_cgroup      = tcp_proto_cgroup,
    #endif
};
EXPORT_SYMBOL(tcp_prot);

6.3 TCP接收函数

6.3.1 tcp_recvmsg核心实现

tcp_recvmsg() (net/ipv4/tcp.c:1566):

c 复制代码
int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                size_t len, int nonblock, int flags, int *addr_len)
{
    struct tcp_sock *tp = tcp_sk(sk);
    int copied = 0;
    u32 peek_seq;
    u32 *seq;
    unsigned long used;
    int err;
    int target;     /* Read at least this many bytes */
    long timeo;
    struct task_struct *user_recv = NULL;
    struct sk_buff *skb, *last;
    u32 urg_hole = 0;

    if (unlikely(flags & MSG_ERRQUEUE))
        return inet_recv_error(sk, msg, len, addr_len);

    if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
        (sk->sk_state == TCP_ESTABLISHED))
        sk_busy_loop(sk, nonblock);

    lock_sock(sk);  // 获取socket锁

    err = -ENOTCONN;
    if (sk->sk_state == TCP_LISTEN)
        goto out;

    timeo = sock_rcvtimeo(sk, nonblock);  // 获取超时时间

    /* 紧急数据处理 */
    if (unlikely(tp->urg_data)) {
        u32 urg_offset = tp->urg_seq - tp->copied_seq;
        if (tp->urg_data && !urg_offset) {
            if (!sock_flag(sk, SOCK_URGINLINE)) {
                ++tp->copied_seq;
                urg_hole++;
                if (!len)
                    goto out;
            }
        }
    }

    seq = &tp->copied_seq;
    if (flags & MSG_PEEK) {
        peek_seq = tp->copied_seq;
        seq = &peek_seq;
    }

    target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);

    do {
        u32 offset;

        /* 检查是否有错误 */
        if (sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN) ||
            !timeo ||
            signal_pending(current))
            break;

        /* Next get a buffer. */
        last = skb_peek_tail(&sk->sk_receive_queue);

        // 遍历接收队列
        skb_queue_walk(&sk->sk_receive_queue, skb) {
            last = skb;

            /* 计算在skb中的偏移量 */
            offset = *seq - TCP_SKB_CB(skb)->seq;

            /* 检查是否有FIN */
            if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
                goto found_fin_ok;

            /* 跳过已处理的数据 */
            if (offset < skb->len)
                goto found_ok_skb;
        }

        /* 没有找到数据 */
        if (copied >= target && !sk->sk_backlog.tail)
            break;

        if (copied) {
            if (sk->sk_err ||
                sk->sk_state == TCP_CLOSE ||
                (sk->sk_shutdown & RCV_SHUTDOWN) ||
                !timeo ||
                signal_pending(current))
                break;
        } else {
            if (sock_flag(sk, SOCK_DONE))
                break;

            if (sk->sk_err) {
                copied = sock_error(sk);
                break;
            }

            if (sk->sk_shutdown & RCV_SHUTDOWN)
                break;

            if (sk->sk_state == TCP_CLOSE) {
                if (!sock_flag(sk, SOCK_DONE)) {
                    copied = -ENOTCONN;
                    break;
                }
                break;
            }

            if (!timeo) {
                copied = -EAGAIN;
                break;
            }

            if (signal_pending(current)) {
                copied = sock_intr_errno(timeo);
                break;
            }
        }

        tcp_cleanup_rbuf(sk, copied);

        if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) {
            /* 安装用户缓冲区到ucopy,用于直接拷贝优化 */
            if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) {
                user_recv = current;
                tp->ucopy.task = user_recv;
                tp->ucopy.iov = msg->msg_iov;
            }

            tp->ucopy.len = len;

            WARN_ON(tp->copied_seq != tp->rcv_nxt &&
                   !(flags & (MSG_PEEK | MSG_TRUNC)));
        }

        /* 等待数据到达 */
        sk_wait_data(sk, &timeo, last);

        if (user_recv) {
            int chunk;

            /* 进程被唤醒后,检查是否有数据被直接拷贝到用户空间 */
            chunk = len - tp->ucopy.len;
            if (chunk != 0) {
                NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk);
                len -= chunk;
                copied += chunk;
            }

            if (tp->rcv_nxt == tp->copied_seq &&
                !skb_queue_empty(&sk->sk_receive_queue)) {
                tcp_prequeue_process(sk);

                chunk = len - tp->ucopy.len;
                if (chunk != 0) {
                    NET_ADD_STATS_USER(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk);
                    len -= chunk;
                    copied += chunk;
                }
            }
        }

        if ((flags & MSG_PEEK) &&
            (peek_seq - copied - urg_hole != tp->copied_seq)) {
            net_dbg_ratelimited("TCP(%s:%d): Application bug, race in MSG_PEEK\n",
                               current->comm,
                               task_pid_nr(current));
            peek_seq = tp->copied_seq;
        }
        continue;

    found_ok_skb:
        /* 找到了数据 */
        used = skb->len - offset;
        if (len < used)
            used = len;

        /* 检查紧急数据 */
        if (tp->urg_data) {
            u32 urg_offset = tp->urg_seq - *seq;
            if (urg_offset < used) {
                if (!urg_offset) {
                    if (!sock_flag(sk, SOCK_URGINLINE)) {
                        ++*seq;
                        urg_hole++;
                        offset++;
                        used--;
                        if (!used)
                            goto skip_copy;
                    }
                } else
                    used = urg_offset;
            }
        }

        /* 拷贝数据到用户空间 */
        if (!(flags & MSG_TRUNC)) {
            err = skb_copy_datagram_msg(skb, offset, msg, used);
            if (err) {
                if (!copied)
                    copied = -EFAULT;
                break;
            }
        }

        *seq += used;
        copied += used;
        len -= used;

        tcp_rcv_space_adjust(sk);

skip_copy:
        /* 处理FIN */
        if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
            goto found_fin_ok;

        /* 如果不是MSG_PEEK,消费skb */
        if (!(flags & MSG_PEEK))
            sk_eat_skb(sk, skb);
        continue;

    found_fin_ok:
        /* 处理FIN */
        ++*seq;
        if (!(flags & MSG_PEEK))
            sk_eat_skb(sk, skb);
        break;
    } while (len > 0);

    if (user_recv) {
        if (!skb_queue_empty(&sk->sk_receive_queue))
            tcp_prequeue_process(sk);

        tp->ucopy.task = NULL;
        tp->ucopy.len = 0;
    }

    /* 清理接收缓冲区,可能发送ACK */
    tcp_cleanup_rbuf(sk, copied);

    release_sock(sk);  // 释放socket锁
    return copied;

out:
    release_sock(sk);
    return err;
}

关键步骤总结:

  1. lock_sock() - 获取socket锁
  2. 遍历sk->sk_receive_queue查找数据
  3. 如果没有数据,调用sk_wait_data()阻塞等待
  4. 找到数据后,调用skb_copy_datagram_msg()拷贝到用户空间
  5. 更新copied_seq序号
  6. 调用tcp_cleanup_rbuf()清理并可能发送ACK
  7. release_sock() - 释放socket锁
6.3.2 进程阻塞等待

sk_wait_data() (net/core/sock.c:2078):

c 复制代码
int sk_wait_data(struct sock *sk, long *timeo, const struct sk_buff *skb)
{
    int rc;
    DEFINE_WAIT(wait);

    // 准备等待队列
    prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
    set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);

    // 等待条件:有新数据到达
    rc = sk_wait_event(sk, timeo,
                      skb_peek_tail(&sk->sk_receive_queue) != skb);

    clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags);
    finish_wait(sk_sleep(sk), &wait);
    return rc;
}

sk_wait_event宏 (include/net/sock.h:936):

c 复制代码
#define sk_wait_event(__sk, __timeo, __condition)           \
    ({  int __rc;                                            \
        release_sock(__sk);        /* 释放socket锁 */      \
        __rc = __condition;                                  \
        if (!__rc) {                                         \
            *(__timeo) = schedule_timeout(*(__timeo));       /* 进程睡眠 */ \
        }                                                    \
        sched_annotate_sleep();                              \
        lock_sock(__sk);           /* 重新获取socket锁 */   \
        __rc = __condition;                                  \
        __rc;                                                \
    })

等待队列机制:

  1. 进程将自己加入sk->sk_wq->wait等待队列
  2. 设置进程状态为TASK_INTERRUPTIBLE
  3. 调用schedule_timeout()让出CPU
  4. 当数据到达时,sk_data_ready()唤醒等待队列上的进程
  5. 进程被唤醒后,重新检查条件并获取socket锁

6.4 数据拷贝机制

6.4.1 skb到用户空间拷贝

skb_copy_datagram_msg() - wrapper:

c 复制代码
int skb_copy_datagram_msg(const struct sk_buff *skb, int offset,
                         struct msghdr *msg, int len)
{
    return skb_copy_datagram_iter(skb, offset, &msg->msg_iter, len);
}

skb_copy_datagram_iovec() (net/core/datagram.c:393):

c 复制代码
int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
                            struct iovec *to, int len)
{
    int start = skb_headlen(skb);
    int i, copy = start - offset;
    struct sk_buff *frag_iter;
    int end = start + skb->data_len;

    trace_skb_copy_datagram_iovec(skb, len);

    /* 拷贝线性数据部分 */
    if (copy > 0) {
        if (copy > len)
            copy = len;
        if (memcpy_toiovec(to, skb->data + offset, copy))
            goto fault;
        if ((len -= copy) == 0)
            return 0;
        offset += copy;
    }

    /* 拷贝分页数据(paged fragments) */
    for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
        int end;
        const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];

        WARN_ON(start > offset + len);

        end = start + skb_frag_size(frag);
        if ((copy = end - offset) > 0) {
            int err;
            u8  *vaddr;
            struct page *page = skb_frag_page(frag);

            if (copy > len)
                copy = len;

            // 映射高端内存页
            vaddr = kmap(page);
            err = memcpy_toiovec(to, vaddr + frag->page_offset +
                                offset - start, copy);
            kunmap(page);

            if (err)
                goto fault;
            if (!(len -= copy))
                return 0;
            offset += copy;
        }
        start = end;
    }

    /* 拷贝分片skb链表 */
    skb_walk_frags(skb, frag_iter) {
        int end;

        WARN_ON(start > offset + len);

        end = start + frag_iter->len;
        if ((copy = end - offset) > 0) {
            if (copy > len)
                copy = len;
            if (skb_copy_datagram_iovec(frag_iter,
                                       offset - start,
                                       to, copy))
                goto fault;
            if ((len -= copy) == 0)
                return 0;
            offset += copy;
        }
        start = end;
    }

    if (!len)
        return 0;

fault:
    return -EFAULT;
}
6.4.2 内核到用户空间拷贝

memcpy_toiovec() (lib/iovec.c:36):

c 复制代码
int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
{
    while (len > 0) {
        if (iov->iov_len) {
            int copy = min_t(unsigned int, iov->iov_len, len);

            // 关键:从内核空间拷贝到用户空间
            if (copy_to_user(iov->iov_base, kdata, copy))
                return -EFAULT;

            kdata += copy;
            len -= copy;
            iov->iov_len -= copy;
            iov->iov_base += copy;
        }
        iov++;
    }

    return 0;
}

copy_to_user(): 架构相关的内核到用户空间拷贝函数

  • x86: arch/x86/include/asm/uaccess.h
  • ARM: arch/arm/include/asm/uaccess.h

在x86-64上:

c 复制代码
static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)
{
    int sz = __compiletime_object_size(from);

    might_fault();

    if (likely(sz < 0 || sz >= n))
        n = _copy_to_user(to, from, n);
    else
        copy_to_user_overflow();

    return n;
}
6.4.3 ucopy直接拷贝优化

tcp_copy_to_iovec() (net/ipv4/tcp_input.c):

c 复制代码
static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen)
{
    struct tcp_sock *tp = tcp_sk(sk);
    int chunk = skb->len - hlen;
    int err;

    local_bh_enable();  // 允许软中断

    // 直接从软中断上下文拷贝到用户空间
    if (skb_csum_unnecessary(skb))
        err = skb_copy_datagram_iovec(skb, hlen,
                                     tp->ucopy.iov, chunk);
    else
        err = skb_copy_and_csum_datagram_iovec(skb,
                                              hlen,
                                              tp->ucopy.iov);

    if (!err) {
        tp->ucopy.len -= chunk;
        tp->copied_seq += chunk;
        tcp_rcv_space_adjust(sk);
    }

    local_bh_disable();
    return err;
}

优势:

  • 避免数据入队再出队的开销
  • 减少内存拷贝次数
  • 降低延迟

6.5 接收缓冲区管理

6.5.1 tcp_cleanup_rbuf

tcp_cleanup_rbuf() (net/ipv4/tcp.c:1387):

c 复制代码
void tcp_cleanup_rbuf(struct sock *sk, int copied)
{
    struct tcp_sock *tp = tcp_sk(sk);
    bool time_to_ack = false;

    struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);

    WARN(skb && !before(tp->copied_seq, TCP_SKB_CB(skb)->end_seq),
         "cleanup rbuf bug: copied %X seq %X rcvnxt %X\n",
         tp->copied_seq, TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt);

    /* 判断是否需要发送ACK */
    if (inet_csk_ack_scheduled(sk)) {
        const struct inet_connection_sock *icsk = inet_csk(sk);

        /* 延迟ACK超时 */
        if (icsk->icsk_ack.blocked ||
            /* 拷贝的数据超过MSS的一半 */
            tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||
            /*
             * pingpong模式下快速响应
             */
            ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED2) ||
             ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED) &&
              !icsk->icsk_ack.pingpong)) ||
            (tp->rx_opt.quickack))
            time_to_ack = true;
    }

    /* 窗口更新 */
    if (copied > 0 || time_to_ack) {
        __u32 rcv_window_now = tcp_receive_window(tp);

        /* 如果窗口增长显著,发送窗口更新 */
        if (2*rcv_window_now > tp->window_clamp) {
            __u32 new_window = __tcp_select_window(sk);

            if (new_window && new_window >= 2 * rcv_window_now)
                time_to_ack = true;
        }
    }

    if (time_to_ack)
        tcp_send_ack(sk);
}
6.5.2 接收窗口调整

tcp_rcv_space_adjust() (net/ipv4/tcp_input.c:563):

c 复制代码
static void tcp_rcv_space_adjust(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);
    int time;
    int copied;

    time = tcp_time_stamp - tp->rcvq_space.time;
    if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0)
        return;

    /* 计算应用消费速率 */
    copied = tp->copied_seq - tp->rcvq_space.seq;
    if (copied <= tp->rcvq_space.space)
        goto new_measure;

    /* 动态调整接收缓冲区大小 */
    if (sysctl_tcp_moderate_rcvbuf &&
        !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) {
        int rcvwin, rcvmem, rcvbuf;

        rcvwin = (copied << 1) + 16 * tp->advmss;

        if (rcvwin > tp->window_clamp)
            rcvwin = tp->window_clamp;

        rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER);
        while (tcp_win_from_space(rcvmem) < tp->advmss)
            rcvmem += 128;

        rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]);
        if (rcvbuf > sk->sk_rcvbuf) {
            sk->sk_rcvbuf = rcvbuf;
            tp->window_clamp = rcvwin;
        }
    }
    tp->rcvq_space.space = copied;

new_measure:
    tp->rcvq_space.seq = tp->copied_seq;
    tp->rcvq_space.time = tcp_time_stamp;
}

6.6 完整调用链总结

6.6.1 报文接收完整调用链(硬件→内核)
复制代码
══════════════════════════════════════════════════════════════════
                     TCP报文接收完整调用链
══════════════════════════════════════════════════════════════════

[1] 硬件层 (NIC)
    └─→ 数据包到达网卡
        └─→ DMA写入Ring Buffer
            └─→ 触发硬件中断(IRQ)

[2] 硬件中断处理 (HARDIRQ上下文)
    文件: drivers/net/ethernet/intel/e1000/e1000_main.c (示例)
    └─→ e1000_intr()                          [驱动中断处理]
        ├─→ 读取中断状态寄存器
        ├─→ 禁用网卡中断
        └─→ napi_schedule(&adapter->napi)     [调度NAPI]
            └─→ __napi_schedule()
                ├─→ 将napi加入per-CPU的poll_list
                └─→ __raise_softirq_irqoff(NET_RX_SOFTIRQ)

[3] 软中断处理 (SOFTIRQ上下文)
    文件: net/core/dev.c
    └─→ net_rx_action()                       [软中断处理函数:5503]
        └─→ napi_poll()
            └─→ driver->poll()                [驱动poll函数]
                └─→ e1000_clean()
                    └─→ e1000_clean_rx_irq()
                        ├─→ 从RX Ring读取描述符
                        ├─→ netdev_alloc_skb() [分配sk_buff]
                        ├─→ memcpy()           [DMA数据→skb]
                        ├─→ eth_type_trans()   [设置协议]
                        └─→ netif_receive_skb() [提交到协议栈:4451]

[4] 网络设备层 (SOFTIRQ上下文)
    文件: net/core/dev.c
    └─→ __netif_receive_skb()                 [:4383]
        └─→ __netif_receive_skb_core()        [:4237]
            ├─→ skb_reset_network_header()
            ├─→ 处理VLAN标签
            ├─→ tcpdump抓包钩子(ptype_all)
            ├─→ RX handler (bonding/bridge)
            └─→ deliver_ptype_list_skb()      [协议分发]
                └─→ pt_prev->func()
                    = ip_rcv()                [IP协议处理]

[5] 网络层 - IP处理 (SOFTIRQ上下文)
    文件: net/ipv4/ip_input.c
    └─→ ip_rcv()                              [:382]
        ├─→ 验证IP头
        ├─→ 验证IP校验和
        └─→ NF_HOOK(NF_INET_PRE_ROUTING)     [Netfilter钩子]
            └─→ ip_rcv_finish()               [:314]
                ├─→ tcp_v4_early_demux()      [Early Demux优化]
                │   └─→ __inet_lookup_established() [socket查找]
                ├─→ ip_route_input_noref()    [路由查找]
                └─→ dst_input()
                    └─→ ip_local_deliver()    [:246]
                        ├─→ ip_defrag()       [IP分片重组]
                        └─→ NF_HOOK(NF_INET_LOCAL_IN)
                            └─→ ip_local_deliver_finish() [:191]
                                ├─→ __skb_pull() [剥离IP头]
                                ├─→ raw_local_deliver() [RAW socket]
                                └─→ ipprot->handler()
                                    = tcp_v4_rcv() [TCP协议处理]

[6] 传输层 - TCP处理 (SOFTIRQ上下文)
    文件: net/ipv4/tcp_ipv4.c, net/ipv4/tcp_input.c
    └─→ tcp_v4_rcv()                          [:1657]
        ├─→ 验证TCP头
        ├─→ 验证TCP校验和
        ├─→ 初始化TCP_SKB_CB
        ├─→ __inet_lookup_skb()               [Socket查找]
        │   └─→ __inet_lookup()
        │       ├─→ __inet_lookup_established() [已建立连接]
        │       └─→ __inet_lookup_listener()    [监听socket]
        ├─→ bh_lock_sock_nested()             [锁定socket]
        └─→ tcp_v4_do_rcv()                   [:1483]
            └─→ tcp_rcv_established()         [已建立连接:5287]
                ├─→ Header Prediction检查
                ├─→ [快速路径]
                │   ├─→ tcp_copy_to_iovec()   [ucopy优化]
                │   │   └─→ 直接拷贝到用户空间
                │   ├─→ tcp_queue_rcv()       [入接收队列:4455]
                │   │   ├─→ tcp_try_coalesce() [尝试合并]
                │   │   └─→ __skb_queue_tail(&sk->sk_receive_queue)
                │   ├─→ tcp_ack()             [处理ACK]
                │   └─→ sk->sk_data_ready()   [唤醒进程]
                │       = sock_def_readable() [:2363]
                │           └─→ wake_up_interruptible_sync_poll()
                └─→ [慢速路径]
                    └─→ tcp_data_queue()      [:4530]
                        ├─→ [顺序包]
                        │   ├─→ tcp_queue_rcv()
                        │   ├─→ tcp_ofo_queue() [处理乱序队列]
                        │   └─→ sk->sk_data_ready()
                        └─→ [乱序包]
                            └─→ tcp_data_queue_ofo() [:4273]
                                └─→ 插入红黑树out_of_order_queue

══════════════════════════════════════════════════════════════════
6.6.2 应用读取完整调用链(用户空间→内核)
复制代码
══════════════════════════════════════════════════════════════════
                  应用层读取TCP数据完整调用链
══════════════════════════════════════════════════════════════════

[1] 用户空间应用
    └─→ recv(sockfd, buffer, len, flags)
        或 read(sockfd, buffer, len)
        或 recvfrom(sockfd, buffer, len, flags, NULL, NULL)
        或 recvmsg(sockfd, &msg, flags)

[2] C库包装 (glibc)
    └─→ syscall(__NR_recvfrom, ...)
        └─→ 触发软件中断 int 0x80 / syscall

[3] 系统调用层 (进程上下文)
    文件: net/socket.c
    └─→ SYSCALL_DEFINE6(recvfrom)            [:1864]
        ├─→ sockfd_lookup_light()            [根据fd查找socket]
        ├─→ 构造msghdr和iovec
        ├─→ sock_recvmsg()                   [:828]
        │   └─→ __sock_recvmsg()
        │       └─→ security_socket_recvmsg() [LSM安全检查]
        │           └─→ __sock_recvmsg_nosec()
        │               └─→ sock->ops->recvmsg()
        │                   = inet_recvmsg() [INET层]
        └─→ move_addr_to_user()              [拷贝地址到用户空间]

[4] INET层 (进程上下文)
    文件: net/ipv4/af_inet.c
    └─→ inet_recvmsg()                       [:762]
        ├─→ sock_rps_record_flow()           [RPS流记录]
        └─→ sk->sk_prot->recvmsg()
            = tcp_recvmsg()                  [TCP协议层]

[5] TCP协议层 (进程上下文)
    文件: net/ipv4/tcp.c
    └─→ tcp_recvmsg()                        [:1566]
        ├─→ lock_sock(sk)                    [获取socket锁]
        ├─→ 检查socket状态
        ├─→ 设置ucopy机制
        │   ├─→ tp->ucopy.task = current
        │   └─→ tp->ucopy.iov = msg->msg_iov
        ├─→ [循环处理]
        │   ├─→ skb_queue_walk(&sk->sk_receive_queue) [遍历接收队列]
        │   │   ├─→ 计算offset = *seq - TCP_SKB_CB(skb)->seq
        │   │   └─→ if (offset < skb->len) goto found_ok_skb
        │   │
        │   ├─→ [未找到数据]
        │   │   └─→ sk_wait_data()           [:2078]
        │   │       ├─→ prepare_to_wait()    [加入等待队列]
        │   │       └─→ sk_wait_event()
        │   │           ├─→ release_sock()   [释放socket锁]
        │   │           ├─→ schedule_timeout() [进程睡眠]
        │   │           └─→ lock_sock()      [重新获取锁]
        │   │
        │   └─→ [找到数据: found_ok_skb]
        │       ├─→ 计算可用数据量used
        │       ├─→ 处理紧急数据
        │       ├─→ skb_copy_datagram_msg()  [拷贝数据]
        │       │   └─→ skb_copy_datagram_iter()
        │       │       └─→ skb_copy_datagram_iovec() [:393]
        │       │           ├─→ memcpy_toiovec() [线性数据]
        │       │           │   └─→ copy_to_user() [内核→用户]
        │       │           ├─→ kmap(page)     [映射分页数据]
        │       │           │   └─→ memcpy_toiovec()
        │       │           │       └─→ copy_to_user()
        │       │           └─→ kunmap(page)
        │       ├─→ *seq += used              [更新copied_seq]
        │       ├─→ copied += used
        │       ├─→ tcp_rcv_space_adjust()   [调整接收窗口]
        │       └─→ sk_eat_skb()             [释放已消费skb]
        │
        ├─→ tcp_cleanup_rbuf()                [:1387]
        │   ├─→ 判断是否需要发送ACK
        │   └─→ tcp_send_ack()               [发送ACK]
        └─→ release_sock(sk)                 [释放socket锁]

[6] 返回用户空间
    └─→ 返回值: 接收的字节数(>0) 或 错误码(<0)

══════════════════════════════════════════════════════════════════

7. 模块层次关系总结

7.1 纵向层次结构

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                         用户空间                                │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │  应用程序 (recv/read/recvfrom/recvmsg)                   │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
                            ↕ 系统调用
┌─────────────────────────────────────────────────────────────────┐
│                         内核空间                                │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L7: 系统调用层                                           │  │
│  │     sys_recvfrom, sys_recvmsg                            │  │
│  │     文件: net/socket.c                                   │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L6: VFS层                                                │  │
│  │     sock_recvmsg, __sock_recvmsg                         │  │
│  │     文件: net/socket.c                                   │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L5: Socket层                                             │  │
│  │     struct socket, struct socket_wq                      │  │
│  │     inet_recvmsg (proto_ops)                             │  │
│  │     文件: include/linux/net.h, net/ipv4/af_inet.c        │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L4: 传输层 (TCP/UDP)                                     │  │
│  │     struct sock, struct tcp_sock                         │  │
│  │     tcp_recvmsg, tcp_data_queue (proto)                  │  │
│  │     文件: include/net/sock.h, include/linux/tcp.h        │  │
│  │          net/ipv4/tcp.c, net/ipv4/tcp_input.c            │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L3: 网络层 (IP)                                          │  │
│  │     ip_rcv, ip_local_deliver, tcp_v4_rcv                 │  │
│  │     文件: net/ipv4/ip_input.c, net/ipv4/tcp_ipv4.c       │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L2: 链路层 (Ethernet)                                    │  │
│  │     __netif_receive_skb, eth_type_trans                  │  │
│  │     文件: net/core/dev.c, net/ethernet/eth.c             │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L1: NAPI/软中断层                                        │  │
│  │     net_rx_action, napi_poll                             │  │
│  │     文件: net/core/dev.c                                 │  │
│  └──────────────────────────────────────────────────────────┘  │
│                            ↕                                    │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │ L0: 网卡驱动层                                           │  │
│  │     driver->poll, netif_receive_skb                      │  │
│  │     文件: drivers/net/ethernet/                          │  │
│  └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                            ↕ DMA + 中断
┌─────────────────────────────────────────────────────────────────┐
│                         硬件层                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  网卡 (NIC), RX Ring, DMA引擎                            │  │
│  └──────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

7.2 关键数据结构关系

复制代码
┌────────────────────────────────────────────────────────────────┐
│                    TCP数据结构层次关系                         │
└────────────────────────────────────────────────────────────────┘

file (struct file)
  └─→ file->private_data
      └─→ socket (struct socket)
          ├─→ socket->sk
          │   └─→ sock (struct sock)
          │       ├─→ sk_receive_queue (struct sk_buff_head)
          │       │   └─→ skb链表 (struct sk_buff)
          │       ├─→ sk_backlog
          │       │   └─→ skb链表
          │       └─→ [TCP specific]
          │           └─→ tcp_sock (struct tcp_sock)
          │               ├─→ out_of_order_queue (rb_root)
          │               │   └─→ 红黑树skb节点
          │               ├─→ ucopy (直接拷贝优化)
          │               └─→ TCP状态机变量
          └─→ socket->ops (const struct proto_ops *)
              └─→ inet_stream_ops
                  └─→ .recvmsg = inet_recvmsg

sock->sk_prot (struct proto *)
  └─→ tcp_prot
      └─→ .recvmsg = tcp_recvmsg

7.3 协议分发机制

复制代码
┌────────────────────────────────────────────────────────────────┐
│                      协议分发层次                              │
└────────────────────────────────────────────────────────────────┘

[L2 → L3] 以太网类型分发
  ptype_base[ETH_P_IP & PTYPE_HASH_MASK]
    └─→ ip_packet_type (struct packet_type)
        └─→ .func = ip_rcv

[L3 → L4] IP协议号分发
  inet_protos[IPPROTO_TCP]
    └─→ tcp_protocol (struct net_protocol)
        ├─→ .early_demux = tcp_v4_early_demux
        └─→ .handler = tcp_v4_rcv

[L4 → Socket] 四元组哈希查找
  tcp_hashinfo
    ├─→ ehash (已建立连接)
    │   └─→ __inet_lookup_established(saddr,sport,daddr,dport)
    └─→ listening_hash (监听socket)
        └─→ __inet_lookup_listener(daddr,dport)

8. 关键数据结构速查

8.1 核心结构体对照表

结构体 文件路径 行号 用途 关键字段
struct sk_buff include/linux/skbuff.h 667 网络数据包 data, len, protocol, sk
struct net_device include/linux/netdevice.h 1675 网络设备 name, napi_list, netdev_ops
struct napi_struct include/linux/netdevice.h 323 NAPI轮询 poll, poll_list, weight
struct packet_type include/linux/netdevice.h 2382 L2协议分发 type, func
struct net_protocol include/net/protocol.h 41 L3协议分发 handler, early_demux
struct socket include/linux/net.h 107 通用Socket sk, ops, wq
struct sock include/net/sock.h 303 协议Socket sk_receive_queue, sk_prot
struct tcp_sock include/linux/tcp.h 138 TCP Socket rcv_nxt, copied_seq, ucopy
struct proto include/net/sock.h 832 协议操作集 recvmsg, sendmsg
struct proto_ops include/linux/net.h 145 Socket操作集 recvmsg, sendmsg

8.2 关键函数对照表

8.2.1 接收路径
函数 文件路径 行号 层次 功能
e1000_intr drivers/net/ethernet/intel/e1000/ - 硬件中断 网卡中断处理
napi_schedule include/linux/netdevice.h 452 中断 调度NAPI
net_rx_action net/core/dev.c 5503 软中断 软中断处理
netif_receive_skb net/core/dev.c 4451 L2入口 提交到协议栈
__netif_receive_skb_core net/core/dev.c 4237 L2 核心接收处理
eth_type_trans net/ethernet/eth.c 193 L2 以太网类型识别
ip_rcv net/ipv4/ip_input.c 382 L3入口 IP接收入口
ip_rcv_finish net/ipv4/ip_input.c 314 L3 IP接收完成
tcp_v4_early_demux net/ipv4/tcp_ipv4.c 1545 L3优化 Early Demux优化
ip_local_deliver net/ipv4/ip_input.c 246 L3 本地交付
ip_local_deliver_finish net/ipv4/ip_input.c 191 L3 L3→L4分发
tcp_v4_rcv net/ipv4/tcp_ipv4.c 1657 L4入口 TCP接收入口
tcp_v4_do_rcv net/ipv4/tcp_ipv4.c 1483 L4 TCP处理分发
tcp_rcv_established net/ipv4/tcp_input.c 5287 L4 已建立连接处理
tcp_data_queue net/ipv4/tcp_input.c 4530 L4 数据入队
tcp_queue_rcv net/ipv4/tcp_input.c 4455 L4 顺序包入队
tcp_data_queue_ofo net/ipv4/tcp_input.c 4273 L4 乱序包处理
sock_def_readable net/core/sock.c 2363 唤醒 唤醒等待进程
8.2.2 读取路径
函数 文件路径 行号 层次 功能
sys_recvfrom net/socket.c 1864 系统调用 recvfrom系统调用
sock_recvmsg net/socket.c 828 VFS Socket接收消息
inet_recvmsg net/ipv4/af_inet.c 762 INET INET层接收
tcp_recvmsg net/ipv4/tcp.c 1566 TCP TCP接收核心
sk_wait_data net/core/sock.c 2078 阻塞 等待数据到达
skb_copy_datagram_iovec net/core/datagram.c 393 拷贝 skb→用户空间
memcpy_toiovec lib/iovec.c 36 拷贝 内核→用户拷贝
tcp_cleanup_rbuf net/ipv4/tcp.c 1387 清理 清理接收缓冲区
tcp_rcv_space_adjust net/ipv4/tcp_input.c 563 优化 调整接收窗口

8.3 重要宏定义

c 复制代码
// TCP控制块访问
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))

// IP控制块访问
#define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))

// 协议类型
#define ETH_P_IP    0x0800    // IPv4
#define ETH_P_IPV6  0x86DD    // IPv6
#define ETH_P_ARP   0x0806    // ARP

// IP协议号
#define IPPROTO_TCP     6     // TCP
#define IPPROTO_UDP    17     // UDP
#define IPPROTO_ICMP    1     // ICMP

// Socket类型
#define SOCK_STREAM     1     // TCP
#define SOCK_DGRAM      2     // UDP
#define SOCK_RAW        3     // RAW

// TCP标志位
#define TCPHDR_FIN  0x01
#define TCPHDR_SYN  0x02
#define TCPHDR_RST  0x04
#define TCPHDR_PSH  0x08
#define TCPHDR_ACK  0x10
#define TCPHDR_URG  0x20

8.4 关键配置参数

8.4.1 sysctl参数
参数 文件 默认值 说明
netdev_budget net/core/dev.c 300 单次软中断最多处理包数
netdev_budget_usecs net/core/dev.c 2000 软中断最大运行时间(us)
sysctl_tcp_rmem net/ipv4/tcp.c [4K,87K,6M] TCP接收缓冲区(min,default,max)
sysctl_tcp_wmem net/ipv4/tcp.c [4K,16K,4M] TCP发送缓冲区(min,default,max)
sysctl_tcp_moderate_rcvbuf net/ipv4/tcp.c 1 自动调整接收缓冲区
sysctl_tcp_low_latency net/ipv4/tcp.c 0 低延迟模式
sysctl_ip_early_demux net/ipv4/ip_input.c 1 Early Demux优化
8.4.2 编译时常量
c 复制代码
#define MAX_SKB_FRAGS         17     // 最大skb分片数
#define NET_IP_ALIGN          2      // IP对齐字节数
#define NET_SKB_PAD           64     // skb padding
#define PTYPE_HASH_SIZE       16     // 协议哈希表大小
#define MAX_INET_PROTOS       256    // 最大INET协议数

8. 性能优化技术总结

8.1 接收路径优化

8.1.1 NAPI (New API)

目的: 减少中断开销,提高批量处理效率

机制:

  • 硬件中断只触发一次,关闭网卡中断
  • 使用软中断轮询处理多个数据包
  • 处理完成后重新开启网卡中断

效果:

  • 高负载下显著降低CPU占用
  • 避免中断风暴
  • 提高吞吐量

相关代码 : net/core/dev.c:5503 (net_rx_action)

8.1.2 GRO (Generic Receive Offload)

目的: 聚合小包,减少协议栈处理次数

机制:

  • 在NAPI层聚合属于同一流的多个小包
  • 聚合后的大包一次性送入协议栈
  • 减少协议处理开销

效果:

  • 提高吞吐量(尤其对小包)
  • 降低CPU占用

相关函数 : napi_gro_receive()

8.1.3 RPS/RFS (Receive Packet/Flow Steering)

目的: 多核负载均衡

机制:

  • RPS: 根据包哈希分发到不同CPU
  • RFS: 将包分发到运行应用的CPU(提高缓存命中)

效果:

  • 多核并行处理
  • 提高缓存局部性

相关配置 : /sys/class/net/eth0/queues/rx-*/rps_cpus

8.1.4 Early Demux

目的: 提前socket查找和路由缓存

机制:

  • 在ip_rcv_finish()中提前查找socket
  • 缓存路由信息到skb->dst
  • 后续处理直接使用缓存

效果:

  • 避免重复路由查找
  • 显著提升已建立连接的性能

相关代码 : net/ipv4/tcp_ipv4.c:1545 (tcp_v4_early_demux)

8.2 TCP层优化

8.2.1 Header Prediction (头部预测)

目的: 快速路径处理常见情况

机制:

c 复制代码
if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
    TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
    !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))

条件:

  • 标志位匹配预期
  • 序号顺序
  • ACK在窗口内

效果:

  • 绝大部分数据包走快速路径
  • 避免复杂状态机处理

相关代码 : net/ipv4/tcp_input.c:5287

8.2.2 ucopy优化

目的: 直接拷贝到用户空间,避免入队

机制:

c 复制代码
if (tp->ucopy.task == current &&
    tp->copied_seq == tp->rcv_nxt &&
    len - tcp_header_len <= tp->ucopy.len &&
    sock_owned_by_user(sk))

条件:

  • 用户进程持有socket锁
  • 数据顺序
  • 用户缓冲区足够

效果:

  • 数据直接从软中断上下文拷贝到用户空间
  • 避免入队→出队开销
  • 降低延迟

相关代码 : net/ipv4/tcp_input.c (tcp_rcv_established)

8.2.3 TCP Coalescing (包合并)

目的: 减少skb数量,降低内存占用

机制:

  • 连续的小包合并到同一个skb
  • 使用tcp_try_coalesce()

效果:

  • 减少内存分配
  • 减少队列长度
  • 提高缓存效率

相关代码 : net/ipv4/tcp_input.c (tcp_try_coalesce)

8.2.4 动态接收窗口调整

目的: 自动调整缓冲区大小

机制:

c 复制代码
tcp_rcv_space_adjust(sk);
  • 根据应用消费速率调整
  • 最大不超过sysctl_tcp_rmem[2]

效果:

  • 充分利用网络带宽
  • 避免缓冲区浪费

相关代码 : net/ipv4/tcp_input.c:563

8.3 内存管理优化

8.3.1 skb克隆 vs 拷贝

skb_clone(): 只克隆skb头部,共享数据

  • 用于:抓包、多路分发
  • 开销小,速度快

skb_copy(): 完整拷贝整个skb

  • 用于:需要修改数据的场景
  • 开销大
8.3.2 分页数据 (Paged Data)

机制:

  • 数据存储在独立的内存页中
  • skb_shared_info->frags[]指向这些页
  • 避免大量连续内存分配

效果:

  • 减少内存碎片
  • 支持零拷贝
8.3.3 per-CPU变量

softnet_data: 每CPU一个实例

c 复制代码
DECLARE_PER_CPU_ALIGNED(struct softnet_data, softnet_data);

效果:

  • 避免缓存行bouncing
  • 无需锁保护
  • 提高并发性能

9. 常见问题诊断

9.1 性能问题

9.1.1 软中断CPU占用高

现象 : /proc/softirqs中NET_RX很高,top显示%si

可能原因:

  1. 网卡中断绑定不合理
  2. RPS/RFS未配置
  3. 网卡驱动参数不当(RX ring太小)

诊断:

bash 复制代码
# 查看软中断统计
cat /proc/softirqs

# 查看网卡中断
cat /proc/interrupts | grep eth0

# 查看网卡统计
ethtool -S eth0

# 查看中断亲和性
cat /proc/irq/*/smp_affinity_list

优化:

bash 复制代码
# 配置RPS
echo "f" > /sys/class/net/eth0/queues/rx-0/rps_cpus

# 增大RX ring
ethtool -G eth0 rx 4096

# 调整netdev_budget
sysctl -w net.core.netdev_budget=600
9.1.2 丢包问题

查看丢包统计:

bash 复制代码
# 网卡驱动层丢包
ethtool -S eth0 | grep drop

# 内核协议栈丢包
netstat -s | grep -i drop
cat /proc/net/softnet_stat

# TCP层丢包
ss -s

常见原因:

  1. rx_dropped: 接收队列满 → 增大sk_rcvbuf
  2. backlog_drop: 后备队列满 → 应用消费太慢
  3. time_squeeze: 软中断预算耗尽 → 增大netdev_budget

9.2 延迟问题

9.2.1 禁用Nagle算法
c 复制代码
int flag = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
9.2.2 启用TCP_QUICKACK
c 复制代码
int flag = 1;
setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag));
9.2.3 调整tcp_low_latency
bash 复制代码
# 禁用prequeue,降低延迟但增加CPU占用
sysctl -w net.ipv4.tcp_low_latency=1

10. 调试技巧

10.1 内核跟踪工具

10.1.1 ftrace
bash 复制代码
# 跟踪TCP接收函数
cd /sys/kernel/debug/tracing
echo function > current_tracer
echo tcp_v4_rcv > set_ftrace_filter
echo 1 > tracing_on

# 查看trace
cat trace
10.1.2 perf
bash 复制代码
# 跟踪网络函数
perf record -e net:* -a -g sleep 10
perf report

# 跟踪特定函数
perf probe --add tcp_recvmsg
perf record -e probe:tcp_recvmsg -a
10.1.3 SystemTap
stap 复制代码
probe kernel.function("tcp_v4_rcv") {
    printf("TCP packet from %s:%d to %s:%d\n",
           ip_ntop(kernel_int($saddr)),
           kernel_int($sport),
           ip_ntop(kernel_int($daddr)),
           kernel_int($dport))
}
10.1.4 BPF/eBPF
bash 复制代码
# 使用bcc工具
# 跟踪TCP连接
tcpconnect

# 跟踪TCP生命周期
tcplife

# 跟踪TCP重传
tcpretrans

10.2 内核日志

10.2.1 启用TCP调试
c 复制代码
// 编译时启用
#define DEBUG 1

// 运行时启用
echo 1 > /proc/sys/net/ipv4/tcp_debug
10.2.2 netconsole
bash 复制代码
# 将内核日志通过网络发送
modprobe netconsole netconsole=6666@192.168.1.100/eth0,6666@192.168.1.1/00:11:22:33:44:55

10.3 抓包分析

bash 复制代码
# tcpdump抓包
tcpdump -i eth0 -w capture.pcap 'tcp port 80'

# wireshark分析
wireshark capture.pcap

# tshark命令行分析
tshark -r capture.pcap -Y "tcp.analysis.flags"

11. 参考资料

11.1 源代码文件索引

核心文件
  • net/core/dev.c - 网络设备核心
  • net/core/skbuff.c - sk_buff管理
  • net/core/sock.c - Socket核心
  • net/socket.c - Socket系统调用
IPv4
  • net/ipv4/af_inet.c - INET地址族
  • net/ipv4/ip_input.c - IP输入
  • net/ipv4/tcp.c - TCP核心
  • net/ipv4/tcp_input.c - TCP输入
  • net/ipv4/tcp_ipv4.c - TCP IPv4
头文件
  • include/linux/skbuff.h - sk_buff定义
  • include/linux/netdevice.h - 网络设备
  • include/net/sock.h - Socket定义
  • include/linux/tcp.h - TCP定义
  • include/net/tcp.h - TCP函数

11.2 重要文档

内核文档
  • Documentation/networking/ - 网络子系统文档
  • Documentation/networking/scaling.txt - 网络扩展
  • Documentation/networking/ip-sysctl.txt - sysctl参数
RFC标准
  • RFC 793 - TCP协议
  • RFC 1122 - Internet主机需求
  • RFC 2018 - TCP SACK
  • RFC 5681 - TCP拥塞控制
  • RFC 7323 - TCP扩展

11.3 书籍推荐

  1. 《深入理解Linux网络技术内幕》 - Christian Benvenuti

    • 全面深入的Linux网络实现
  2. 《Linux内核源代码情景分析》 - 毛德操、胡希明

    • 详细的源代码分析
  3. 《TCP/IP详解 卷1:协议》 - W. Richard Stevens

    • TCP/IP协议基础
  4. 《Linux内核设计的艺术》 - 新设计团队

    • 内核架构和设计思想

11.4 在线资源


附录A: 编译和调试环境搭建

A.1 编译内核

bash 复制代码
# 下载源码
wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.0.tar.xz
tar xf linux-3.10.0.tar.xz
cd linux-3.10.0

# 配置
make menuconfig
# 启用: Kernel hacking -> Kernel debugging
#      Kernel hacking -> Compile kernel with debug info

# 编译
make -j$(nproc)

# 安装
sudo make modules_install
sudo make install

A.2 配置调试选项

bash 复制代码
# 在.config中启用
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_FRAME_POINTER=y
CONFIG_KGDB=y
CONFIG_KGDB_KDB=y

A.3 使用QEMU调试

bash 复制代码
# 启动QEMU
qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -initrd /boot/initrd.img \
    -append "console=ttyS0 nokaslr" \
    -s -S

# 在另一个终端用GDB连接
gdb vmlinux
(gdb) target remote :1234
(gdb) break tcp_v4_rcv
(gdb) continue

附录B: 缩略语表

缩略语 全称 中文
NIC Network Interface Card 网络接口卡
DMA Direct Memory Access 直接内存访问
IRQ Interrupt Request 中断请求
NAPI New API 新API
GRO Generic Receive Offload 通用接收卸载
RPS Receive Packet Steering 接收包导向
RFS Receive Flow Steering 接收流导向
RSS Receive Side Scaling 接收端扩展
TSO TCP Segmentation Offload TCP分段卸载
GSO Generic Segmentation Offload 通用分段卸载
LRO Large Receive Offload 大接收卸载
MTU Maximum Transmission Unit 最大传输单元
MSS Maximum Segment Size 最大段大小
RTT Round Trip Time 往返时间
SACK Selective Acknowledgment 选择性确认
ECN Explicit Congestion Notification 显式拥塞通知
SYN Synchronize 同步
ACK Acknowledgment 确认
FIN Finish 结束
RST Reset 重置

--

文档结束

相关推荐
lihui_cbdd1 天前
AMBER 24 生产环境部署完全指南(5090可用)
linux·计算化学
生活很暖很治愈1 天前
Linux基础开发工具
linux·服务器·git·vim
似霰1 天前
Linux Shell 脚本编程——核心基础语法
linux·shell
LUCIFER1 天前
[驱动进阶——MIPI摄像头驱动(五)]rk3588+OV13855摄像头驱动加载过程详细解析第四部分——ISP驱动
linux·驱动开发
暮云星影1 天前
四、linux系统 应用开发:UI开发环境配置概述 (一)
linux·ui·arm
a程序小傲1 天前
得物Java面试被问:RocketMQ的消息轨迹追踪实现
java·linux·spring·面试·职场和发展·rocketmq·java-rocketmq
Ghost Face...1 天前
i386 CPU页式存储管理深度解析
java·linux·服务器
LEEE@FPGA1 天前
zynq 是不是有了设备树,再linux中不需要编写驱动也能控制
linux·运维·单片机
RisunJan1 天前
Linux命令-less(分页查看器)
linux·运维
梁正雄1 天前
linux服务-MariaDB 10.6 Galera Cluster+garbd
linux·运维·mariadb