2,856
社区成员




在 Linux 内核中开发网络驱动时,需要理解并操作一系列关键的接口和数据结构。这些组件构成了驱动与内核网络子系统之间的桥梁,直接影响驱动的性能和功能。以下是核心接口和数据结构的详细解析:
struct net_device
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()
动态分配该结构,避免直接操作。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处理等)
};
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()
将数据包注入内核。ndo_start_xmit()
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;
}
netdev_tx_lock()
/netdev_tx_unlock()
**:锁定/解锁发送队列,确保线程安全。netif_stop_queue()
/netif_wake_queue()
**:暂停/恢复发送队列(如网卡忙时暂停队列)。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);
}
napi_add()
。napi_schedule()
调度NAPI处理。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;
}
register_netdev()
/unregister_netdev()
**:注册/注销网络设备。alloc_etherdev()
**:分配以太网设备(自动初始化常用字段)。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;
}
netdev_init_scheduler()
**:初始化调度器,支持多队列。netdev_rx_queue_add()
**:添加接收队列。napi_gro_receive()
和 napi_gro_complete()
实现。vlan_dev_add_vlan()
/vlan_dev_remove_vlan()
**:添加/删除VLAN设备。vlan_put_tag()
/vlan_get_tag()
**:操作VLAN标签。dev->stats
**:维护设备统计信息(如接收/发送数据包数、错误数)。dev->stats.rx_packets++;
dev->stats.rx_bytes += len;
printk()
**:内核日志输出。dev_dbg()
/dev_err()
**:设备特定的调试/错误日志。ethtool
**:通过实现 ndo_ethtool_ops
支持用户空间调试工具。开发 Linux 网络驱动时,核心是理解 net_device
、net_device_ops
和 sk_buff
三者的关系,并正确实现发送和接收路径的关键回调函数。对于高性能场景,需重点关注 NAPI、多队列和 GRO 等优化机制。通过合理使用这些接口和数据结构,可构建出稳定、高效的网络驱动。