网络设备驱动:原理与架构
网络驱动在内核中的位置
用户空间 socket()
│
▼ 协议栈(TCP/UDP/IP)
│
▼ 通用网络层
net_device
│
▼ 网络设备驱动
ndo_start_xmit() ←── 发送
netif_rx() / napi_schedule() ──► 接收
│
▼ 硬件(以太网 MAC + PHY)核心数据结构
net_device
c
struct net_device {
char name[IFNAMSIZ]; /* "eth0", "wlan0" */
unsigned char dev_addr[MAX_ADDR_LEN]; /* MAC 地址 */
const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
unsigned int mtu;
unsigned int flags; /* IFF_UP, IFF_BROADCAST... */
struct net_device_stats stats; /* 统计计数器 */
/* ... */
};sk_buff(Socket Buffer)
网络数据包的核心载体:
c
struct sk_buff {
struct sk_buff *next, *prev;
struct sock *sk;
struct net_device *dev;
/* 数据指针 */
unsigned char *head; /* 分配的内存起始 */
unsigned char *data; /* 有效数据起始 */
unsigned char *tail; /* 有效数据结束 */
unsigned char *end; /* 分配的内存结束 */
unsigned int len; /* 数据长度 */
__be16 protocol; /* ETH_P_IP, ETH_P_ARP... */
/* ... */
};sk_buff 操作:
c
/* 在头部预留空间(添加协议头) */
skb_push(skb, sizeof(struct ethhdr));
/* 移除头部 */
skb_pull(skb, sizeof(struct ethhdr));
/* 在尾部添加数据 */
skb_put(skb, data_len);
/* 获取各层头部 */
struct ethhdr *eth = eth_hdr(skb);
struct iphdr *ip = ip_hdr(skb);完整网络驱动框架
c
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
struct mynet_priv {
struct net_device *ndev;
void __iomem *base;
int irq;
struct napi_struct napi;
spinlock_t tx_lock;
struct sk_buff *tx_skb;
};
/* ===== 发送路径 ===== */
static netdev_tx_t mynet_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct mynet_priv *priv = netdev_priv(ndev);
unsigned long flags;
spin_lock_irqsave(&priv->tx_lock, flags);
/* 检查硬件发送队列是否满 */
if (tx_queue_full(priv)) {
netif_stop_queue(ndev); /* 停止上层继续发包 */
spin_unlock_irqrestore(&priv->tx_lock, flags);
return NETDEV_TX_BUSY;
}
/* 将 skb 数据写入硬件 TX FIFO 或 DMA 描述符 */
write_to_hw_tx(priv, skb->data, skb->len);
/* 更新统计 */
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
dev_kfree_skb(skb); /* 发送后释放 skb */
spin_unlock_irqrestore(&priv->tx_lock, flags);
return NETDEV_TX_OK;
}
/* ===== 接收路径(NAPI) ===== */
/* NAPI poll 函数:在软中断上下文中批量收包 */
static int mynet_poll(struct napi_struct *napi, int budget)
{
struct mynet_priv *priv = container_of(napi, struct mynet_priv, napi);
struct net_device *ndev = priv->ndev;
int received = 0;
while (received < budget && rx_data_available(priv)) {
struct sk_buff *skb;
int pkt_len = get_rx_packet_len(priv);
skb = netdev_alloc_skb_ip_align(ndev, pkt_len);
if (!skb) {
ndev->stats.rx_dropped++;
break;
}
/* 从硬件读取数据到 skb */
read_from_hw_rx(priv, skb_put(skb, pkt_len), pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += pkt_len;
napi_gro_receive(napi, skb); /* 提交给协议栈(支持 GRO) */
received++;
}
/* 如果本轮处理完所有包,退出 NAPI 轮询 */
if (received < budget) {
napi_complete_done(napi, received);
enable_rx_interrupt(priv); /* 重新开启接收中断 */
}
return received;
}
/* 中断处理:触发 NAPI 轮询 */
static irqreturn_t mynet_isr(int irq, void *dev_id)
{
struct mynet_priv *priv = dev_id;
u32 status = read_irq_status(priv);
if (status & RX_INT) {
disable_rx_interrupt(priv); /* 关闭接收中断,避免风暴 */
napi_schedule(&priv->napi); /* 调度 NAPI poll */
}
if (status & TX_INT) {
/* 发送完成,唤醒发送队列 */
netif_wake_queue(priv->ndev);
}
clear_irq_status(priv, status);
return IRQ_HANDLED;
}
/* ===== 设备控制 ===== */
static int mynet_open(struct net_device *ndev)
{
struct mynet_priv *priv = netdev_priv(ndev);
napi_enable(&priv->napi);
enable_hardware(priv);
netif_start_queue(ndev);
return 0;
}
static int mynet_stop(struct net_device *ndev)
{
struct mynet_priv *priv = netdev_priv(ndev);
netif_stop_queue(ndev);
disable_hardware(priv);
napi_disable(&priv->napi);
return 0;
}
static const struct net_device_ops mynet_ops = {
.ndo_open = mynet_open,
.ndo_stop = mynet_stop,
.ndo_start_xmit = mynet_start_xmit,
.ndo_get_stats64 = mynet_get_stats64,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
/* ===== probe ===== */
static int mynet_probe(struct platform_device *pdev)
{
struct net_device *ndev;
struct mynet_priv *priv;
int ret;
/* 分配 net_device(含私有数据) */
ndev = alloc_etherdev(sizeof(*priv));
if (!ndev)
return -ENOMEM;
priv = netdev_priv(ndev);
priv->ndev = ndev;
SET_NETDEV_DEV(ndev, &pdev->dev);
/* 初始化 NAPI */
netif_napi_add(ndev, &priv->napi, mynet_poll);
ndev->netdev_ops = &mynet_ops;
/* 读取 MAC 地址(从 EEPROM 或设备树) */
eth_hw_addr_random(ndev); /* 随机 MAC(开发用) */
ret = register_netdev(ndev);
if (ret) {
netif_napi_del(&priv->napi);
free_netdev(ndev);
return ret;
}
platform_set_drvdata(pdev, ndev);
netdev_info(ndev, "mynet registered\n");
return 0;
}NAPI 工作原理
NAPI(New API)解决了高速网络下中断风暴问题:
数据包到达
│
▼ 硬件中断
关闭 RX 中断
│
▼ napi_schedule()
软中断(NET_RX_SOFTIRQ)
│
▼ mynet_poll(budget=64)
批量处理最多 64 个包
│
├── 处理完所有包 → napi_complete() → 重新开启 RX 中断
└── 达到 budget → 下次软中断继续处理ethtool 支持
c
static void mynet_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
strscpy(info->driver, "mynet", sizeof(info->driver));
strscpy(info->version, "1.0", sizeof(info->version));
}
static const struct ethtool_ops mynet_ethtool_ops = {
.get_drvinfo = mynet_get_drvinfo,
.get_link = ethtool_op_get_link,
};
/* 在 probe 中设置 */
ndev->ethtool_ops = &mynet_ethtool_ops;bash
# 用户空间查询
ethtool eth0
ethtool -i eth0 # 驱动信息
ethtool -S eth0 # 统计信息