编写 Linux 网络驱动时,需要关注哪些关键的接口和数据结构?

闪客一族 2025-06-19 15:29:53

编写 Linux 网络驱动时,需要关注哪些关键的接口和数据结构?

...全文
503 1 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复

在 Linux 内核中开发网络驱动时,需要理解并操作一系列关键的接口和数据结构。这些组件构成了驱动与内核网络子系统之间的桥梁,直接影响驱动的性能和功能。以下是核心接口和数据结构的详细解析:

一、核心数据结构

1. struct net_device

  • 作用:表示一个网络设备的抽象,包含设备的基本信息(如名称、MAC地址)、状态和操作函数集。
  • 关键字段
    struct net_device {
        char name[IFNAMSIZ];           // 设备名称(如eth0)
        unsigned char dev_addr[ETH_ALEN]; // MAC地址
        unsigned int flags;           // 设备标志(如IFF_UP、IFF_RUNNING)
        unsigned int mtu;             // 最大传输单元
        unsigned int tx_queue_len;    // 发送队列长度
        const struct net_device_ops *netdev_ops; // 操作函数集
        struct netdev_queue *_tx;     // 发送队列
        struct netdev_bus_info bus_info; // 总线信息
        // ... 其他字段
    };
    
  • 注意:现代驱动通常使用 alloc_netdev()alloc_etherdev() 动态分配该结构,避免直接操作。

2. struct net_device_ops

  • 作用:定义网络设备的操作函数集,驱动必须实现其中的关键回调函数。
  • 核心函数
    struct net_device_ops {
        int (*ndo_init)(struct net_device *dev);    // 设备初始化
        void (*ndo_uninit)(struct net_device *dev);  // 设备释放
        int (*ndo_open)(struct net_device *dev);    // 设备打开
        int (*ndo_stop)(struct net_device *dev);    // 设备关闭
        netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,  // 发送数据包
                                      struct net_device *dev);
        int (*ndo_set_mac_address)(struct net_device *dev,  // 设置MAC地址
                                   void *addr);
        // ... 其他函数(如多播、VLAN处理等)
    };
    

3. struct sk_buff(Socket Buffer)

  • 作用:内核中表示网络数据包的核心数据结构,贯穿整个网络栈。
  • 关键字段
    struct sk_buff {
        struct sk_buff *next;         // 链表指针
        unsigned char *head, *data, *tail, *end; // 数据缓冲区指针
        unsigned int len, data_len;   // 数据长度
        __be16 protocol;              // 协议类型(如ETH_P_IP)
        struct net_device *dev;       // 关联的网络设备
        // ... 其他字段(如时间戳、VLAN标签、校验和等)
    };
    
  • 注意:驱动负责填充或解析该结构,并通过 netif_rx()napi_gro_receive() 将数据包注入内核。

二、发送路径关键接口

1. ndo_start_xmit()

  • 功能:驱动发送数据包的核心回调函数,当上层协议(如TCP/IP)有数据需要发送时调用。
  • 实现要点
    netdev_tx_t mydriver_start_xmit(struct sk_buff *skb, struct net_device *dev) {
        // 1. 从skb中提取数据和元信息
        unsigned char *data = skb->data;
        unsigned int len = skb->len;
        
        // 2. 硬件操作:将数据写入网卡发送缓冲区
        mydriver_hw_tx(dev, data, len);
        
        // 3. 释放skb(驱动已接管数据)
        dev_kfree_skb(skb);
        
        // 4. 返回发送状态(通常为NETDEV_TX_OK)
        return NETDEV_TX_OK;
    }
    

2. 发送队列管理

  • **netdev_tx_lock()/netdev_tx_unlock()**:锁定/解锁发送队列,确保线程安全。
  • **netif_stop_queue()/netif_wake_queue()**:暂停/恢复发送队列(如网卡忙时暂停队列)。

三、接收路径关键接口

1. 轮询模式(传统方式)

  • **netif_rx()**:将接收到的数据包注入内核网络栈。
    void mydriver_rx_handler(struct net_device *dev, unsigned char *data, int len) {
        // 1. 分配skb
        struct sk_buff *skb = dev_alloc_skb(len + NET_IP_ALIGN);
        if (!skb) return;
        
        // 2. 复制数据到skb
        skb_reserve(skb, NET_IP_ALIGN);
        memcpy(skb_put(skb, len), data, len);
        
        // 3. 设置协议类型和设备
        skb->dev = dev;
        skb->protocol = eth_type_trans(skb, dev);
        
        // 4. 提交到内核
        netif_rx(skb);
    }
    

2. NAPI(New API,高性能方式)

  • 作用:通过轮询替代中断,减少高流量下的中断开销。
  • 关键步骤
    1. 注册NAPI:在驱动初始化时调用 napi_add()
    2. 中断处理:触发中断时调用 napi_schedule() 调度NAPI处理。
    3. 轮询处理:实现 napi_poll() 回调函数处理批量数据包。
      static int mydriver_poll(struct napi_struct *napi, int budget) {
       struct net_device *dev = container_of(napi, struct mydriver, napi);
       int packets = 0;
       
       // 从硬件FIFO读取多个数据包
       while (packets < budget && mydriver_hw_rx_ready(dev)) {
           struct sk_buff *skb = mydriver_fetch_packet(dev);
           if (skb) {
               napi_gro_receive(napi, skb);
               packets++;
           }
       }
       
       // 如果处理完所有数据包,关闭NAPI轮询
       if (!mydriver_hw_rx_ready(dev)) {
           napi_complete(napi);
       }
       
       return packets;
      }
      

四、设备初始化与资源管理

1. 设备注册与注销

  • **register_netdev()/unregister_netdev()**:注册/注销网络设备。
  • **alloc_etherdev()**:分配以太网设备(自动初始化常用字段)。

2. 中断管理

  • **request_irq()**:注册中断处理函数。
  • **free_irq()**:释放中断资源。
  • 示例
    static irqreturn_t mydriver_irq_handler(int irq, void *dev_id) {
        struct net_device *dev = dev_id;
        
        // 检查中断类型并处理
        if (mydriver_hw_irq_is_rx(dev)) {
            // 接收中断处理
            mydriver_rx(dev);
        } else if (mydriver_hw_irq_is_tx(dev)) {
            // 发送完成中断处理
            mydriver_tx_complete(dev);
        }
        
        return IRQ_HANDLED;
    }
    

五、高级特性接口

1. 多队列支持

  • **netdev_init_scheduler()**:初始化调度器,支持多队列。
  • **netdev_rx_queue_add()**:添加接收队列。

2. GRO(Generic Receive Offload)

  • 作用:将多个小包合并为一个大包,减少上层处理开销。
  • 接口:通过 napi_gro_receive()napi_gro_complete() 实现。

3. VLAN支持

  • **vlan_dev_add_vlan()/vlan_dev_remove_vlan()**:添加/删除VLAN设备。
  • **vlan_put_tag()/vlan_get_tag()**:操作VLAN标签。

六、调试与性能优化

1. 统计信息

  • **dev->stats**:维护设备统计信息(如接收/发送数据包数、错误数)。
  • 更新示例
    dev->stats.rx_packets++;
    dev->stats.rx_bytes += len;
    

2. 调试工具

  • **printk()**:内核日志输出。
  • **dev_dbg()/dev_err()**:设备特定的调试/错误日志。
  • **ethtool**:通过实现 ndo_ethtool_ops 支持用户空间调试工具。

总结

开发 Linux 网络驱动时,核心是理解 net_devicenet_device_opssk_buff 三者的关系,并正确实现发送和接收路径的关键回调函数。对于高性能场景,需重点关注 NAPI、多队列和 GRO 等优化机制。通过合理使用这些接口和数据结构,可构建出稳定、高效的网络驱动。

5,284

社区成员

发帖
与我相关
我的任务
社区描述
本论坛以AI、WoS 、XR、IoT、Auto、生成式AI等核心板块组成,为开发者提供便捷及高效的学习和交流平台。 高通开发者专区主页:https://qualcomm.csdn.net/
人工智能物联网机器学习 技术论坛(原bbs) 北京·东城区
社区管理员
  • csdnsqst0050
  • chipseeker
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧