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,接口消失。
关键实现说明
驱动通过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,仅覆盖网络设备驱动的最小必要接口。