网络驱动开发:从sk_buff到NAPI收包流程 - 第1节
1.1 Linux 网络驱动模型概述
Linux 网络驱动位于内核协议栈与物理网络设备之间,承担着数据帧的收发、设备管理、中断处理等核心职责。理解其整体模型,是掌握网络驱动开发的基石。
1.1.1 驱动模型分层架构
Linux 网络驱动遵循“内核抽象层 → 驱动核心层 → 硬件操作层”的三层架构,每一层职责清晰,接口稳定。
核心分层:
- 网络协议栈接口层:通过
struct net_device与内核协议栈交互,提供ndo_start_xmit、netif_rx等标准接口。 - 驱动核心层:管理
sk_buff缓冲区、NAPI 调度、中断处理、统计计数。 - 硬件操作层:直接操作 DMA 引擎、MAC 寄存器、PHY 芯片,完成数据帧的物理收发。
1.1.2 关键数据结构关系
网络驱动中三个核心数据结构构成驱动骨架:
| 数据结构 | 作用域 | 核心字段 / 方法 |
|---|---|---|
struct net_device | 网络设备抽象 | name, dev_addr, netdev_ops, features |
struct sk_buff | 网络缓冲区 | data, len, dev, protocol, cb |
struct napi_struct | NAPI 轮询实例 | poll, weight, state, dev_list |
驱动开发要点: 驱动注册时需填充
net_device_ops 中的 ndo_open、ndo_stop、ndo_start_xmit 等回调,协议栈通过这些回调驱动硬件工作。
1.2 OSI 与 TCP/IP 协议栈模型
网络驱动处于物理层和数据链路层,向上为网络层提供统一接口。理解 OSI 与 TCP/IP 的映射关系,有助于定位驱动在协议栈中的位置。
1.2.1 分层对比
| OSI 七层模型 | TCP/IP 四层模型 | 典型协议 / 组件 | 驱动关注点 |
|---|---|---|---|
| 应用层 | 应用层 | HTTP, FTP, DNS | — |
| 表示层 | SSL/TLS, MIME | — | |
| 会话层 | RPC, NetBIOS | — | |
| 传输层 | 传输层 | TCP, UDP | — |
| 网络层 | 网络层 | IP, ICMP, ARP | — |
| 数据链路层 | 网络接口层 | 以太网 MAC, VLAN, LLC | 驱动核心区域 |
| 物理层 | MII, RMII, RGMII, PHY | 硬件操作区域 |
常见误区: 驱动开发者常将“网络接口层”等同于“网卡驱动”,实际上该层包含硬件驱动、MAC 控制器、PHY 以及链路层协议(如 ARP、LLDP)的协同工作。
1.3 驱动与协议栈的接口
驱动与内核协议栈之间通过一组标准化的接口进行双向数据交换。收包方向:硬件 → 驱动 → 协议栈;发包方向:协议栈 → 驱动 → 硬件。
1.3.1 核心接口函数
| 方向 | 接口函数 | 调用者 | 功能描述 |
|---|---|---|---|
| 收包 | netif_rx() / napi_gro_receive() | 驱动中断处理 / NAPI poll | 将 sk_buff 送入协议栈接收队列 |
| 发包 | ndo_start_xmit() | 协议栈 dev_queue_xmit() | 驱动将 sk_buff 映射到 DMA 并触发发送 |
| 设备注册 | register_netdev() | 驱动初始化 | 将 net_device 注册到内核网络子系统 |
| 设备注销 | unregister_netdev() | 驱动卸载 | 从内核移除设备并释放资源 |
| 流量控制 | netif_stop_queue() / netif_wake_queue() | 驱动发送完成 / 发送满 | 控制协议栈是否继续向驱动投递发包请求 |
1.3.2 收包数据流(驱动视角)
- 硬件接收:网卡收到数据帧,通过 DMA 写入预分配的内存缓冲区(Ring Buffer)。
- 中断触发:网卡产生硬件中断,驱动中断处理函数被调用。
- NAPI 调度:驱动关闭中断并加入 NAPI 轮询列表,调用
napi_schedule()。 - sk_buff 构建:在 NAPI
poll回调中,驱动从 Ring Buffer 取出数据,构建struct sk_buff。 - 协议栈递交:调用
napi_gro_receive()或netif_rx()将sk_buff送入协议栈。
1.3.3 发包数据流(驱动视角)
- 协议栈封装:Socket 层 → TCP/UDP → IP → 邻居子系统,最终调用
dev_queue_xmit()。 - 驱动回调:协议栈通过
net_device_ops->ndo_start_xmit()将sk_buff传递给驱动。 - DMA 映射:驱动将
sk_buff中的数据映射到 DMA 描述符,通知 DMA 引擎开始发送。 - 发送完成:硬件发送完成后产生完成中断,驱动释放
sk_buff并更新统计。
关键代码片段:ndo_start_xmit 典型实现骨架
static netdev_tx_t my_ndo_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct my_priv *priv = netdev_priv(dev);
// 停止发送队列(如果硬件 TX 环已满)
if (tx_ring_full(priv))
netif_stop_queue(dev);
// DMA 映射 skb->data
dma_addr_t dma_addr = dma_map_single(dev->dev.parent,
skb->data, skb->len,
DMA_TO_DEVICE);
// 将 DMA 地址填入 TX 描述符,触发发送
tx_desc_fill(priv, dma_addr, skb->len);
// 更新统计
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
// 释放 sk_buff(或在发送完成中断中释放)
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
1.4 本章小结
- Linux 网络驱动采用三层模型:协议栈接口层、驱动核心层、硬件操作层。
- OSI 数据链路层和物理层对应 TCP/IP 网络接口层,是驱动开发的主战场。
- 驱动与协议栈通过
net_device_ops、netif_rx()/napi_gro_receive()、netif_stop_queue()/netif_wake_queue()等接口完成双向数据交换。 - 收包流程的核心路径:硬件 DMA → 中断 → NAPI 调度 → sk_buff 构建 → 协议栈递交。