Linux内核TCP网络模块深度分析
目录
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 up或ip 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;
}
关键步骤:
- 从RX Ring读取描述符,检查DD位(描述符完成)
- 分配sk_buff结构
- DMA数据拷贝到sk_buff
- 调用
eth_type_trans()设置协议 - 调用
netif_receive_skb()提交到协议栈 - 补充新的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);
}
关键优化点:
- Header Prediction: 预测数据包特征,走快速路径
- ucopy优化: 直接拷贝到用户空间,避免入队
- 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;
}
关键步骤总结:
lock_sock()- 获取socket锁- 遍历
sk->sk_receive_queue查找数据 - 如果没有数据,调用
sk_wait_data()阻塞等待 - 找到数据后,调用
skb_copy_datagram_msg()拷贝到用户空间 - 更新
copied_seq序号 - 调用
tcp_cleanup_rbuf()清理并可能发送ACK 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; \
})
等待队列机制:
- 进程将自己加入
sk->sk_wq->wait等待队列 - 设置进程状态为
TASK_INTERRUPTIBLE - 调用
schedule_timeout()让出CPU - 当数据到达时,
sk_data_ready()唤醒等待队列上的进程 - 进程被唤醒后,重新检查条件并获取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高
可能原因:
- 网卡中断绑定不合理
- RPS/RFS未配置
- 网卡驱动参数不当(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
常见原因:
rx_dropped: 接收队列满 → 增大sk_rcvbufbacklog_drop: 后备队列满 → 应用消费太慢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 书籍推荐
-
《深入理解Linux网络技术内幕》 - Christian Benvenuti
- 全面深入的Linux网络实现
-
《Linux内核源代码情景分析》 - 毛德操、胡希明
- 详细的源代码分析
-
《TCP/IP详解 卷1:协议》 - W. Richard Stevens
- TCP/IP协议基础
-
《Linux内核设计的艺术》 - 新设计团队
- 内核架构和设计思想
11.4 在线资源
- Linux Kernel官网: https://www.kernel.org/
- LWN.net: https://lwn.net/ (Linux内核新闻)
- Kernel Newbies: https://kernelnewbies.org/
- Elixir: https://elixir.bootlin.com/ (源代码浏览)
附录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 | 重置 |
--
文档结束