Linux内核分析与驱动编程 Lecture8:网络接口驱动程序

来源

  • 文件:lecture8.pdf(55页,文本型PDF)
  • 云盘路径:/田浩然上传的资料/0C110_Linux内核分析与驱动编程/lecture8/lecture8.pdf
  • 课程:0C110 Linux内核分析与驱动编程(北京大学,lisuke@ss.pku.edu.cn)
  • 主题:网络设备驱动程序

核心内容

1. 网络设备 vs 字符/块设备

  • 网络接口驱动程序与字符设备、块设备驱动程序有本质不同
  • 本次课介绍一个不与真实硬件相关的虚拟网络接口(snull)

2. 设备注册:struct net_device

  • 定义在 <linux/netdevice.h>
  • 必须动态分配:struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device *))
  • 示例:snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d", snull_init);
  • alloc_etherdev() 简化版(定义在 <linux/etherdevice.h>),自动使用 eth%d 命名
  • register_netdev() 注册设备到内核

3. 设备初始化

ether_setup(dev);
dev->open = snull_open;
dev->stop = snull_release;
dev->set_config = snull_config;
dev->hard_start_xmit = snull_tx;
dev->do_ioctl = snull_ioctl;
dev->get_stats = snull_stats;
dev->rebuild_header = snull_rebuild_header;
dev->hard_header = snull_header;
dev->tx_timeout = snull_tx_timeout;
dev->watchdog_timeo = timeout;
dev->flags |= IFF_NOARP;
dev->features |= NETIF_F_NO_CSUM;
dev->hard_header_cache = NULL; /* 禁用缓存 */

4. 私有数据结构 snull_priv

struct snull_priv {
    struct net_device_stats stats;  // 统计信息
    int status;                      // 状态
    struct snull_packet *ppool;
    struct snull_packet *rx_queue;   // 接收包队列
    int rx_int_enabled;
    int tx_packetlen;
    u8 *tx_packetdata;
    struct sk_buff *skb;
    spinlock_t lock;
};

通过 netdev_priv() 获取私有数据区。

5. net_device 重要字段

  • char name[IFNAMSIZ] — 接口名,含 %d 时 register_netdev 替换为唯一编号
  • unsigned long state — 驱动状态
  • struct net_device *next — 全局接口链表
  • int (*init)(struct net_device *) — 初始化函数(现已少用,推荐在 register 前初始化)
  • unsigned long rmem_end/rmem_start/mem_end/mem_start — 接收/发送内存地址
  • unsigned long base_addr — I/O 基地址
  • unsigned char irq — 中断号
  • unsigned char if_port — 多端口设备当前端口
  • unsigned char dma — DMA 通道

6. 数据包发送 snull_tx()

  • 虚拟设备核心行为:将包回环到另一个 snull 接口
  • 修改 IP 头第三字节(C类网段翻转),重建 IP 校验和
  • 模拟接收中断 + 传输完成中断
  • 支持锁死模拟(lockup 参数定时模拟丢包)

7. 中断处理 snull_napi_interrupt()

  • NAPI 兼容的中断处理
  • 检查 SNULL_RX_INTR → 调度 netif_rx_schedule()
  • 检查 SNULL_TX_INTR → 更新统计,释放 skb
  • 使用 spin_lock/spin_unlock 保护

8. 清理工作

void snull_cleanup(void) {
    for (i = 0; i < 2; i++) {
        if (snull_devs[i]) {
            unregister_netdev(snull_devs[i]);
            snull_teardown_pool(snull_devs[i]);
            free_netdev(snull_devs[i]);
        }
    }
}

9. 课后作业

  • 阅读《Linux Device Drivers》第9,10,11,12,13,14,18章
  • 编译加载 snull,观察网络设备是否可用
  • 思考 snull 可完成的特殊任务

关键概念

  • alloc_netdev / register_netdev / unregister_netdev
  • ether_setup / alloc_etherdev
  • netdev_priv / net_device_stats
  • NAPI (netif_rx_schedule)
  • IFF_NOARP / NETIF_F_NO_CSUM
  • spinlock_t 在驱动中的使用

关联

  • 与 Lecture 7(字符设备驱动)对比学习
  • 与《Linux Device Drivers》第9-14章关联
  • snull 源码是理解网络驱动框架的极佳示例