1. 编写最小网络设备驱动框架

编写最小网络设备驱动框架 学习卡片

任务说明

本案例实现一个最小化的网络设备驱动框架,该驱动注册一个虚拟以太网设备到Linux内核,实现网络设备子系统所必需的open、stop、start_xmit三个回调函数,并支持模块的动态加载与卸载。驱动不涉及真实硬件收发,仅用于演示网络设备驱动的完整生命周期与内核接口约定。

驱动代码实现


// min_netdev.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/string.h>

#define DRV_NAME "min_netdev"
#define TX_BUF_LEN 1514

static struct net_device *g_dev;

// 打开设备:分配发送缓冲区
static int min_open(struct net_device *dev)
{
    netif_start_queue(dev);
    pr_info("min_netdev: device %s opened\n", dev->name);
    return 0;
}

// 停止设备:停止发送队列
static int min_stop(struct net_device *dev)
{
    netif_stop_queue(dev);
    pr_info("min_netdev: device %s stopped\n", dev->name);
    return 0;
}

// 发送数据包:直接释放skb,模拟发送完成
static netdev_tx_t min_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    // 记录发送长度后直接释放,模拟数据已发出
    pr_info("min_netdev: transmit skb len=%u on %s\n", skb->len, dev->name);
    dev_kfree_skb(skb);
    return NETDEV_TX_OK;
}

// 网络设备操作集
static const struct net_device_ops min_netdev_ops = {
    .ndo_open       = min_open,
    .ndo_stop       = min_stop,
    .ndo_start_xmit = min_start_xmit,
};

// 模块加载入口
static int __init min_init(void)
{
    int ret;

    // 分配net_device结构体,使用以太网设备分配器
    g_dev = alloc_etherdev(0);
    if (!g_dev) {
        pr_err("min_netdev: failed to alloc net_device\n");
        return -ENOMEM;
    }

    // 设置设备名称与操作集
    strncpy(g_dev->name, "min%d", IFNAMSIZ);
    g_dev->netdev_ops = &min_netdev_ops;

    // 注册网络设备到内核
    ret = register_netdev(g_dev);
    if (ret) {
        pr_err("min_netdev: register_netdev failed, ret=%d\n", ret);
        free_netdev(g_dev);
        g_dev = NULL;
        return ret;
    }

    pr_info("min_netdev: registered %s\n", g_dev->name);
    return 0;
}

// 模块卸载入口
static void __exit min_exit(void)
{
    if (g_dev) {
        unregister_netdev(g_dev);
        free_netdev(g_dev);
        g_dev = NULL;
        pr_info("min_netdev: unregistered and freed\n");
    }
}

module_init(min_init);
module_exit(min_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kernel Course");
MODULE_DESCRIPTION("Minimal network device driver framework");

Makefile


obj-m += min_netdev.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD  := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

驱动加载与验证

将上述两个文件置于同一目录,执行make编译生成min_netdev.ko。使用sudo insmod min_netdev.ko加载模块,加载成功后执行ip link show可观察到名为min0的网络接口。执行sudo ip link set min0 up触发open回调,内核日志(dmesg)应显示设备打开信息;执行sudo ip link set min0 down触发stop回调。使用sudo rmmod min_netdev卸载模块,日志显示卸载信息且ip link中接口消失。

验证判定标准

  • 模块加载成功:lsmod | grep min_netdev有输出,dmesg尾部出现registered min0。
  • 接口存在:ip link show min0能显示接口状态为DOWN。
  • open回调生效:ip link set min0 up后dmesg出现device min0 opened,接口状态变为UP。
  • stop回调生效:ip link set min0 down后dmesg出现device min0 stopped。
  • 模块卸载成功:rmmod min_netdev无报错,dmesg出现unregistered and freed,接口消失。
最小网络设备驱动框架 — 内核注册与回调时序 用户空间 内核网络核心 min_netdev 驱动 虚拟硬件(无) insmod min_netdev.ko register_netdev() ndo_open/stop/xmit 注册成功 ip link set min0 up ndo_open() netif_start_queue() ping/发包 ndo_start_xmit() dev_kfree_skb() rmmod min_netdev unregister_netdev() free_netdev() 加载/卸载 open/stop 数据发送 内核返回

关键实现说明

驱动通过alloc_etherdev分配以太网设备结构,设置netdev_ops操作集后调用register_netdev完成注册。open回调中调用netif_start_queue启用发送队列,stop回调调用netif_stop_queue停止队列,start_xmit回调直接释放skb并返回NETDEV_TX_OK模拟发送完成。模块卸载时依次执行unregister_netdev与free_netdev释放资源。该框架不涉及真实硬件中断、NAPI或DMA,仅覆盖网络设备驱动的最小必要接口。