首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Linux 內核網路協議棧:二層(鏈路層)數據包發送流程深度解析

     編輯:Java 綜合教學

核心入口:dev_queue_xmit()

所有上層協議(IP、ARP 等)最終都呼叫 dev_queue_xmit(struct sk_buff *skb) 將封裝好幀交給驅動。關鍵職責:

  1. GSO 分片處理(netif_needs_gso)
  2. 非線性 skb 線性化(__skb_linearize)
  3. 校驗和計算(skb_checksum_help)
  4. 選擇發送隊列(dev_pick_tx,多隊列網卡)
  5. 進入 Qdisc(排隊規則)或直接驅動發送

Qdisc(排隊規則)兩條路徑


__dev_xmit_skb()
├─ 若 Qdisc 可繞過(TCQ_F_CAN_BYPASS)且隊列空 → sch_direct_xmit() 直發
└─ 否則 → qdisc_enqueue_root() 入隊 → qdisc_run() 排程發送

發送軟中斷 (NET_TX_SOFTIRQ) 角色

  • dev_queue_xmit:封裝處理 + 首次發送嘗試
  • net_tx_action(軟中斷):
     • 發送積壓/失敗重傳的 skb
     • 釋放已發送完成的 skb(dev_kfree_skb_irq 放入 completion_queue)

關鍵函數調用鏈

函數核心動作
qdisc_run()設定 __QDISC_STATE_RUNNING,呼叫 __qdisc_run
__qdisc_run()循環 qdisc_restart,需讓 CPU 或時間片用盡則 __netif_schedule 重排
qdisc_restart()dequeue_skb 取幀 → sch_direct_xmit 發送
sch_direct_xmit()釋放 qdisc lock → 取 netif_tx_lock → dev_hard_start_xmit
dev_hard_start_xmit()呼叫驅動 ndo_start_xmit,更新隊列時間戳

三把鎖的協作關係

  1. qdisc_lock(q):保護 Qdisc 內部 skb 隊列
  2. __QDISC_STATE_RUNNING:位元鎖,確保單 CPU 執行 Qdisc
  3. netif_tx_lock (txq->_xmit_lock):保護驅動發送函數獨佔執行

**規則**:持有 qdisc_lock 時不可持有 netif_tx_lock,反之亦然,sch_direct_xmit 中先釋放 qdisc_lock 再取 netif_tx_lock。

環回裝置特例

static netdev_tx_t loopback_xmit(struct sk_buff *skb, struct net_device *dev) {
    skb_orphan(skb);
    skb->protocol = eth_type_trans(skb, dev);
    netif_rx(skb);  // 直接注入接收路徑,無真實發送
    return NETDEV_TX_OK;
}

原始套接字監聽發送包

dev_queue_xmit_nit() 將克隆的 skb 傳給 ptype_all 鏈表上的 ETH_P_ALL 協議處理器,實現 tcpdump/Wireshark 抓包。

總結圖解


上層協議
    │
    ▼
dev_queue_xmit() ── GSO/線性化/校驗和
    │
    ▼
dev_pick_tx() 選隊列
    │
    ▼
Qdisc (pfifo_fq / mq / noqueue)
    │
    ├─ 可繞過且空 ──▶ sch_direct_xmit() ──▶ 驅動 ndo_start_xmit()
    │
    └─ 入隊 ──▶ qdisc_run() ──▶ 軟中斷 net_tx_action ──▶ qdisc_restart() 循環發送

掌握二層發送路徑、Qdisc 與軟中斷分工、三把鎖協作,是排查網卡驅動效能、丟包、延遲抖動的關鍵基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved