2. 网络设备驱动基础架构
在深入性能优化之前,必须牢固掌握 Linux 网络设备驱动的核心骨架。本章将拆解 net_device 结构体、NAPI 机制、中断处理函数(ISR)以及数据包从硬件到协议栈的完整收发流程。理解这些基础组件是后续实施零拷贝、中断合并等优化策略的前提。
2.1 net_device 结构体:驱动的“身份证”
net_device 是 Linux 网络子系统中最核心的数据结构,每一个网络接口(无论是物理网卡还是虚拟设备)在内核中都对应一个 net_device 实例。它包含了设备的所有属性、操作函数指针以及统计信息。
关键字段分类: 以下表格列出了
net_device 中与驱动开发和性能优化最相关的字段。
| 类别 | 字段名 | 说明 | 性能影响 |
|---|---|---|---|
| 标识 | name |
设备名称(如 eth0) | — |
| 操作函数 | netdev_ops |
指向 struct net_device_ops,包含 ndo_open、ndo_stop、ndo_start_xmit 等 |
发送路径核心回调 |
| NAPI | napi_list |
设备关联的 NAPI 实例链表 | 轮询与中断平衡 |
| 特性 | features |
设备能力标志位(如 NETIF_F_SG、NETIF_F_GRO) |
决定协议栈卸载策略 |
| 队列 | tx_queue_len |
发送队列长度 | 影响发送拥塞控制 |
| 统计 | stats |
struct net_device_stats,包含收发包计数、错误计数 |
监控与调优依据 |
| MTU | mtu |
最大传输单元 | 影响分片与吞吐 |
// net_device 结构体简化定义 (include/linux/netdevice.h)
struct net_device {
char name[IFNAMSIZ];
const struct net_device_ops *netdev_ops;
struct list_head napi_list; // NAPI 实例链表
unsigned long long features; // 设备特性标志
unsigned int tx_queue_len;
struct net_device_stats stats;
unsigned int mtu;
// ... 其他字段
};
优化视角: 在驱动初始化时,正确设置
features 字段(如启用 NETIF_F_GRO 和 NETIF_F_RXHASH)可以直接让协议栈启用硬件卸载和 RSS 分流,这是零成本性能提升的第一步。
2.2 中断处理函数(ISR):第一响应者
当网卡接收到数据包或完成发送时,硬件会触发一个中断信号。CPU 会暂停当前任务,执行注册的中断处理函数(ISR)。ISR 的设计直接决定了系统的实时性和吞吐量。
- 上半部(Top Half): 在中断上下文中执行,必须快速、不可睡眠。典型操作:读取中断状态寄存器、清中断标志、调度下半部(如 NAPI 或 tasklet)。
- 下半部(Bottom Half): 在软中断上下文中执行,可延迟处理。典型操作:将数据包从硬件 FIFO 拷贝到 sk_buff、调用
napi_schedule()启动轮询。
性能陷阱: 如果在 ISR 上半部中执行了过多操作(如内存分配、复杂逻辑),会导致中断延迟增加,甚至引发中断丢失(interrupt storm)。现代高性能驱动几乎全部采用 NAPI 将数据包处理推迟到下半部。
// 典型 ISR 上半部示例
static irqreturn_t my_netdev_interrupt(int irq, void *dev_id) {
struct net_device *dev = dev_id;
struct my_priv *priv = netdev_priv(dev);
// 1. 读取中断状态寄存器
u32 status = readl(priv->ioaddr + INT_STATUS);
// 2. 清中断标志(硬件要求)
writel(status, priv->ioaddr + INT_STATUS);
// 3. 如果收到数据包,调度 NAPI
if (status & RX_INT) {
napi_schedule(&priv->napi);
}
// 4. 如果发送完成,更新统计并唤醒队列
if (status & TX_INT) {
netif_wake_queue(dev);
}
return IRQ_HANDLED;
}
2.3 NAPI:中断与轮询的优雅平衡
NAPI(New API)是 Linux 网络子系统中用于在高吞吐场景下减少中断开销的核心机制。其核心思想是:在流量较低时使用中断唤醒驱动,在流量较高时切换到轮询模式,避免频繁中断导致的 CPU 浪费。
NAPI 工作流程:
- 中断触发,ISR 调用
napi_schedule()。 - NAPI 实例被添加到软中断处理队列
softnet_data的 poll_list 中。 - 软中断
NET_RX_SOFTIRQ被触发,调用 NAPI 的poll回调函数。 poll函数从硬件接收描述符中批量读取数据包,构造sk_buff并送入协议栈。- 如果
poll函数处理完预算(budget)内的所有包,则重新使能中断;否则继续轮询。
// NAPI poll 回调函数模板
static int my_netdev_poll(struct napi_struct *napi, int budget) {
struct my_priv *priv = container_of(napi, struct my_priv, napi);
int work_done = 0;
// 循环从硬件接收环中取出数据包,直到 budget 耗尽或环为空
while (work_done < budget) {
struct sk_buff *skb = my_retrieve_rx_packet(priv);
if (!skb)
break;
// 将 skb 送入协议栈(netif_receive_skb)
netif_receive_skb(skb);
work_done++;
}
// 如果处理完所有包,退出轮询并重新使能中断
if (work_done < budget) {
napi_complete_done(napi, work_done);
my_enable_rx_interrupt(priv);
}
return work_done;
}
优化关键:
budget 参数控制每次 poll 最多处理的数据包数量。增大 budget 可以提升批量处理效率,但会增加单次软中断的延迟。通常建议在 64~256 之间调整,配合中断合并(coalescing)使用效果更佳。
2.4 数据包收发流程:从硬件到协议栈
理解数据包从网卡物理线缆到达内核协议栈的完整路径,是定位性能瓶颈的基础。下图展示了接收路径的核心环节。
2.5 发送路径:反向流程
发送路径相对接收路径更为直接,但同样存在关键优化点。核心函数是 ndo_start_xmit,由协议栈在调用 dev_queue_xmit 时触发。
- 协议栈封装: 上层协议(TCP/UDP)将数据封装成
sk_buff,调用dev_queue_xmit。 - 排队规则:
sk_buff进入设备的发送队列(qdisc),进行流量整形和调度。 - 驱动发送: 驱动
ndo_start_xmit将sk_buff中的数据映射到 DMA 描述符,触发硬件发送。 - 发送完成中断: 硬件发送完成后触发中断,驱动释放
sk_buff并更新统计。
// 驱动发送函数模板
static netdev_tx_t my_netdev_start_xmit(struct sk_buff *skb,
struct net_device *dev) {
struct my_priv *priv = netdev_priv(dev);
// 1. 将 skb 数据映射到 DMA 地址
dma_addr_t dma_addr = dma_map_single(&dev->dev, skb->data,
skb->len, DMA_TO_DEVICE);
// 2. 将 DMA 地址和长度写入硬件发送描述符
my_write_tx_desc(priv, dma_addr, skb->len);
// 3. 触发硬件发送(写 Doorbell 寄存器)
writel(1, priv->ioaddr + TX_DOORBELL);
// 4. 如果发送环已满,停止队列
if (my_tx_ring_full(priv))
netif_stop_queue(dev);
// 5. 记录 skb 以便在发送完成中断中释放
priv->tx_skb[priv->tx_next] = skb;
return NETDEV_TX_OK;
}
发送路径优化要点:
- DMA 映射开销: 每次发送都调用
dma_map_single有性能损耗。可考虑使用skb_frag_dma_map或预映射缓冲区。 - 发送完成合并: 多个发送完成中断可以合并处理,减少 CPU 负载。
- TSO/GSO 卸载: 如果硬件支持 TCP 分段卸载,驱动应在
features中设置NETIF_F_TSO,让硬件处理大包分段。
2.6 关键数据结构关系图
以下 SVG 展示了 net_device、NAPI、中断、sk_buff 和硬件描述符之间的协作关系。
2.7 本章小结
| 组件 | 核心作用 | 性能关注点 |
|---|---|---|
net_device |
设备抽象,提供操作接口和特性标志 | 正确设置 features 以启用硬件卸载 |
| ISR | 快速响应硬件事件,调度下半部 | 上半部必须极简,避免中断延迟 |
| NAPI | 中断/轮询混合模式,减少高负载下的中断开销 | 调整 budget 和使能/关闭阈值 |
| sk_buff | 内核网络数据包的标准载体 | 避免频繁分配/释放,使用缓存池 |
| DMA 描述符环 | 硬件与内存之间的数据传输通道 | 环大小影响吞吐和延迟,需合理配置 |
下一阶段准备: 在掌握了驱动基础架构后,第 3 章将深入分析传统收发包路径中的性能瓶颈(如内存拷贝、上下文切换、锁竞争),为后续的零拷贝和中断合并策略提供量化依据。