Linux 內核中設備驅動代碼佔比極大,網絡設備驅動作為核心子系統之一,結構相對穩定,且從 2.0 到 2.2、2.4、2.6 乃至現代內核演進中保持較好的兼容性。本文系統梳理網絡驅動的分類、核心數據結構、基本方法、常見難點及調試技巧,為驅動開發者提供全景指南。
| 類型 | 特徵 | 典型設備 |
|---|---|---|
| 字符設備 | 無緩衝、順序存取、不可隨機訪問 | 鍵盤、鼠標、串口、/dev/null |
| 塊設備 | 帶緩衝、隨機存取、可掛載文件系統 | 硬盤、軟盤、CD-ROM、USB 存儲 |
| 網絡設備 | 專用數據結構 sk_buff、協議棧接口、無設備節點 | 以太網卡、WiFi、PPP、虛擬網設備 |
網絡設備不走 open/read/write 文件操作,而是通過 net_device 結構體註冊到網絡子系統,由協議棧統一調度收發。
每個網絡設備對應一個 struct net_device 實例(對象),內含:
priv、狀態標志等。ndo_open、ndo_stop、ndo_start_xmit(發送)、ndo_do_ioctl、ndo_set_mac_address、ndo_change_mtu 等。調用時第一個參數即為 net_device *dev,類似 C++ 的 this 指針,便於訪問私有數據。
用戶空間
↓ socket API (send/recv)
協議棧 (TCP/IP)
↓ dev_queue_xmit() ↑ netif_rx() / napi_gro_receive()
┌─────────────────────────────────────────────┐
│ net_device_ops │
│ ndo_start_xmit (發送) 中斷處理 (接收) │
└─────────────────────────────────────────────┘
↓ 硬件 DMA/寄存器操作 ↑ 硬件中斷/DMA
物理介質 (雙絞線/光纖/無線電波)
sk_buff → dev_queue_xmit → 排隊調度 → 驅動 ndo_start_xmit → 寫入硬件發送環/寄存器 → 硬件發包 → 發送完成中斷 → 釋放 sk_buff。sk_buff、填充數據 → netif_rx (舊) 或 napi_gro_receive (新 NAPI) → 協議棧處理 → 交付用戶空間。module_init 註冊的函數。net_device (alloc_etherdev / alloc_netdev)。netdev_ops、ethtool_ops、dev->priv 指向私有結構。register_netdev(dev) 向核心註冊。int ndo_open(struct net_device *dev) {
// 申請 IRQ(若初始化未申請)
// 啟動硬件接收/發送引擎
// netif_start_queue(dev); // 告訴核心可以發送
return 0;
}
netdev_tx_t ndo_start_xmit(struct sk_buff *skb, struct net_device *dev) {
// 1. 檢查硬件發送隊列是否滿(netif_queue_stopped)
// 2. 將 skb->data 映射到 DMA 區域
// 3. 填充硬件發送描述符
// 4. 觸發硬件發送
// 5. 返回 NETDEV_TX_OK
}
若硬件發送隊列滿,驅動需調用 netif_stop_queue(dev),待發送完成中斷中調用 netif_wake_queue(dev) 恢復。
poll 函數到 softirq 上下文(NET_RX_SOFTIRQ)批量處理,大幅降低 CPU 佔用、提高吞吐。現代驅動必須實現 NAPI。// 中斷處理器
irqreturn_t intr_handler(int irq, void *dev_id) {
struct net_device *dev = dev_id;
// 讀取/清除中斷狀態
napi_schedule(&priv->napi); // 調度 poll
return IRQ_HANDLED;
}
// NAPI poll 函數
int poll(struct napi_struct *napi, int budget) {
int work_done = 0;
while (work_done < budget && has_rx_packets()) {
struct sk_buff *skb = allocate_skb();
// 從硬件拷貝數據到 skb
napi_gro_receive(napi, skb);
work_done++;
}
if (work_done < budget) {
napi_complete_done(napi, work_done); // 重新啟用中斷
}
return work_done;
}
netif_stop_queue(dev) 停止發送隊列。sk_buff。| 結構體 | 用途 |
|---|---|
struct net_device | 設備實例,核心對象 |
struct sk_buff | 數據包緩衝區描述符,含頭部預留、尾部擴展、引用計數、協議頭指針等 |
struct net_device_ops | 方法表,替代舊版直接在 net_device 中的函數指針 |
struct napi_struct | NAPI 輪詢上下文 |
struct ethtool_ops | ethtool 用戶空間查詢/設置接口 |
PCI 設備常共享 IRQ 線。驅動註冊時需指定 IRQF_SHARED,ISR 內部讀取硬件中斷狀態寄存器判斷是否屬於本設備,非本設備立即返回 IRQ_NONE。
netif_stop_queue(dev) → 返回 NETDEV_TX_BUSY(核心會稍後重試)。netif_wake_queue(dev)。netif_tx_queue_stopped、netif_stop_queue/wake_queue。budget 限制每次輪詢處理包數,防止佔用過多 CPU;netif_receive_skb 入口處有 netdev_max_backlog 全局隊列限制。dynamic_debug (dyndbg) + pr_debug/netdev_dbg,運行時通過 /sys/kernel/debug/dynamic_debug/control 啟用特定文件/行/函數的日志,無需重編譯。ndo_get_stats64,配合 ethtool -S ethX 查看詳細收發/錯誤/丟包計數。tcpdump -i ethX -w dump.pcap、wireshark 分析幀結構、重傳、校驗和錯誤。kdump + crash 分析 vmcore;或串口控制台捕獲 oops 信息。ethtool -d ethX(需驅動實現 get_regs)、iomem 映射後直接讀寫調試。netdevices.txt、NAPI.txt、skbuff.txt、driver.rst。drivers/net/ethernet/intel/e1000e/、drivers/net/ethernet/realtek/、drivers/net/wireless/ 等成熟驅動代碼。netdev_ops 替代舊字段、ndo_eth_ioctl 棄用等)。make -C /lib/modules/$(uname -r)/build M=$PWD modules。module_param 導出可在 insmod / modprobe 時配置(如 MAC 地址、調試等級)。ndo_set_rx_mode、suspend/resume 回調支持運行時掛起/喚醒。NETIF_F_HW_CSUM、NETIF_F_TSO、NETIF_F_GRO 等特性標志,大幅降低 CPU 負載。Linux 網絡驅動開發的核心在於理解 net_device 對象模型、sk_buff 生命週期、NAPI 輪詢機制以及與協議棧的契約介面。掌握初始化/打開/發送/接收/關閉五大流程,善用動態調試與統計計數器,參考主流驅動代碼風格,即可編寫出高性能、穩定的網絡驅動。