Linux内核网络驱动核心:net_device结构体
一、net_device:网络设备的“核心身份证”
`net_device`是Linux网络驱动的灵魂,所有网络设备(物理网卡、虚拟接口等)都靠它向内核“报备”。驱动的核心工作,就是初始化这个结构体,再把它注册到内核,让系统能识别、管理设备——相当于字符设备的“cdev+file_operations”,专为网络设备定制。
二、关键成员:按功能拆解,好懂好记
1. 身份标识:设备的“基础信息卡”
- `name`:设备名,像“eth0”“wlan0”,是设备被系统识别的标识。
- `dev_addr`:当前使用的MAC地址,能通过软件修改。
- `perm_addr`:出厂固定的硬件MAC,改不了,是设备的永久标识。
- `addr_len`:MAC地址长度,以太网设备固定为6字节。
2. 硬件资源:设备的“硬件配置单”
- `base_addr`:硬件I/O起始地址,老式设备常用。
- `irq`:设备中断号,告诉CPU什么时候该处理数据。
- `mem_start/mem_end`:设备和内核共享的内存区域,现在用得少。
- `dma`:DMA通道,仅老硬件需要配置,现代网卡自动分配资源,无需手动填。
3. 操作函数集:设备的“功能说明书”(最核心)
- `netdev_ops`:设备核心操作接口,类似“遥控器”,包含:
- `ndo_open()/ndo_stop()`:开启、关闭设备;
- `ndo_start_xmit()`:发包函数,必须返回成功或忙碌,不能偷偷丢包;
- `ndo_set_mac_address()`:修改MAC地址。
- `ethtool_ops`:支持用`ethtool`工具查网速、调参数,是用户配置的入口。
- `header_ops`:处理链路层帧头,控制数据封装和解析格式。
4. 性能与队列:设备的“高速通道配置”
- `_tx`和`num_tx_queues`:发送队列,多队列支持并行发包,提升并发性能。
- `_rx`和`num_rx_queues`:接收队列,配合NAPI技术减少CPU中断,提升收包效率。
- `trans_start`:记录最后一次发包时间,内核靠它判断是否超时、检测故障。
5. 状态与配置:设备的“运行参数”
- `flags`:状态标志,比如`ifF_UP`表示设备开启、`IFF_RUNNING`表示网线插好、`IFF_BROADCAST`表示支持广播、`IFF_MULTICAST`表示支持组播。
- `mtu`:最大传输单元,默认1500,控制单次传输的最大数据包大小。
- `type`:ARP类型,以太网固定为`ARPHRD_ETHER`。
- `hard_header_len`:链路层头长度,以太网固定14字节。
6. PHY管理:设备的“物理层管家”
- `phydev`:关联PHY芯片,负责检测网线状态、调节网卡速率等物理层管理。
- `carrier_changes`:记录载波状态变化,检测热插拔(比如突然插拔网线)。
三、生命周期:从申请到注销,流程不能乱
1. 申请:给设备“办户口”
- 以太网设备用`alloc_etherdev()`或多队列版`alloc_etherdev_mqs()`,函数会自动填充以太网默认参数,推荐优先用。
- 通用设备用`alloc_netdev()`,灵活性高但需手动配更多参数。
2. 初始化:给设备“配功能”
- 绑定操作函数:把`netdev_ops`、`ethtool_ops`赋值给结构体,告诉内核设备能做什么。
- 存私有数据:用`netdev_priv(dev)`拿空间存驱动专属信息(如硬件寄存器地址、缓冲区)。
- 可选配置:设置MAC地址(随机生成或固定写入)。
3. 注册:让设备“正式上岗”
- 用`register_netdev(dev)`注册,返回0成功,负数失败。
- 注册失败要立刻用`free_netdev(dev)`释放资源,避免内存泄漏。
4. 注销与释放:让设备“安全退休”
- 先`unregister_netdev(dev)`:从内核移除,停止所有服务。
- 再`free_netdev(dev)`:释放内存和私有数据。
- 顺序绝对不能乱,否则引发内核错误。
四、避坑关键:性能与稳定性的底线
1. 高性能必用NAPI:用NAPI收包机制,在`ndo_open()`里开启,能大幅减少CPU中断,提升高并发性能。
2. 多队列要配对:现代网卡支持多队列,正确设置配置队列数和实际生效数,才能发挥硬件性能。
3. 发包必须返回状态:`ndo_start_xmit()`不能偷偷丢包,必须返回`NETDEV_TX_OK`(成功)或`NETDEV_TX_BUSY`(队列满),否则内核判定设备故障。
五、极简模板:快速搭好驱动骨架
```c
// 1. 定义设备操作函数:告诉内核设备能做什么
static const struct net_device_ops my_netdev_ops = {
.ndo_open = my_device_open,
.ndo_stop = my_device_stop,
.ndo_start_xmit = my_device_send,
.ndo_set_mac_address = eth_mac_addr,
};
// 2. 驱动初始化:申请→初始化→注册
static int __init my_driver_init(void) {
struct net_device dev;
struct my_priv priv;
dev = alloc_etherdev(sizeof(priv));
if (!dev) return -ENOMEM;
dev->netdev_ops = &my_netdev_ops;
priv = netdev_priv(dev);
if (register_netdev(dev)) {
free_netdev(dev);
return -ENODEV;
}
return 0;
}
// 3. 驱动退出:注销→释放
static void __exit my_driver_exit(void) {
unregister_netdev(dev);
free_netdev(dev);
}
```
这个模板是核心骨架,实际只需在`my_device_open()`、`my_device_send()`里填充硬件操作逻辑,就能快速搭建基础驱动。