网络驱动开发:从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 芯片,完成数据帧的物理收发。
Linux 网络驱动三层架构 Linux 网络驱动三层架构 网络协议栈接口层 struct net_device · ndo_start_xmit · netif_rx · dev_queue_xmit 驱动核心层 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_structNAPI 轮询实例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硬件操作区域
OSI 与 TCP/IP 协议栈映射 OSI 与 TCP/IP 协议栈映射 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 应用层 传输层 网络层 网络接口层 网络驱动位置 数据链路层 + 物理层
常见误区: 驱动开发者常将“网络接口层”等同于“网卡驱动”,实际上该层包含硬件驱动、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 收包数据流(驱动视角)

  1. 硬件接收:网卡收到数据帧,通过 DMA 写入预分配的内存缓冲区(Ring Buffer)。
  2. 中断触发:网卡产生硬件中断,驱动中断处理函数被调用。
  3. NAPI 调度:驱动关闭中断并加入 NAPI 轮询列表,调用 napi_schedule()。
  4. sk_buff 构建:在 NAPI poll 回调中,驱动从 Ring Buffer 取出数据,构建 struct sk_buff。
  5. 协议栈递交:调用 napi_gro_receive() 或 netif_rx() 将 sk_buff 送入协议栈。
驱动与协议栈收包接口流程 驱动与协议栈收包接口流程 ① 硬件接收 DMA → Ring Buffer ② 中断触发 硬件中断 → handler ③ NAPI 调度 napi_schedule() ④ sk_buff 构建 Ring Buffer → sk_buff ⑤ 协议栈递交 napi_gro_receive() ⑥ 协议栈处理 IP → TCP/UDP → Socket 关键接口:napi_schedule() → poll() → napi_gro_receive() / netif_rx()

1.3.3 发包数据流(驱动视角)

  1. 协议栈封装:Socket 层 → TCP/UDP → IP → 邻居子系统,最终调用 dev_queue_xmit()。
  2. 驱动回调:协议栈通过 net_device_ops->ndo_start_xmit() 将 sk_buff 传递给驱动。
  3. DMA 映射:驱动将 sk_buff 中的数据映射到 DMA 描述符,通知 DMA 引擎开始发送。
  4. 发送完成:硬件发送完成后产生完成中断,驱动释放 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 构建 → 协议栈递交。