iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Software Development

從 C++ 菜鳥到 Low-Latency 勇者:一場分秒必爭的賽局系列 第 23

[Day 23] HFT Networking Infrastructure: Kernel Bypass II

  • 分享至 

  • xImage
  •  

Data Plane Development Kit (DPDK) 被歸類在 Kernel Bypass 技術,是一套專門用來做高速網路封包處理的開源軟體套件。其設計原理是讓網路封包盡量繞過 Linux kernel 的傳統網路協定堆疊,直接在 user space 進行高速處理。簡單來說 DPDK 的目標,就是讓程式可以更快速地從網卡接收、消化和送出網路封包。

一般 Linux 網路程式大概是:
網卡 → Linux 網卡驅動程式 → Linux 網路核心 → Socket → 應用程式

這種方式很通用,但封包在經過核心、Socket 等層級時會產生額外開銷。如果網卡速度非常高,且需要處理大量的小封包,這些開銷就可能成為效能瓶頸。

DPDK 則希望讓資料封包更直接地進入 user space 的程式,因此可以大幅降低每個封包的處理成本:
應用程式→ DPDK → 網卡驅動程式 → NIC

以下介紹幾個讓 DPDK 達到高速效能的重要概念。

1. Poll Mode Driver —— 永不停歇的搬運工

在傳統的核心網路驅動中,每個封包的到達都會觸發一次 interrupt 並動態配置記憶體,這對系統帶來了極大的 overhead:
網卡收到封包 → 觸發硬體中斷通知 CPU → CPU 暫停現行工作進行處理

而 DPDK 則是透過核心組件 PMD 徹底翻轉這個流程,以批次處理的方式分攤 CPU 週期。它讓 CPU 採取主動出擊的 polling 機制,持續不間斷地檢查網卡的 receive queue:
CPU
⠀├─ 有封包嗎?
⠀├─ 有封包嗎?
⠀├─ 有封包嗎?
⠀└─ 有封包嗎?

藉由 PMD 的設計,DPDK 能夠以 batching 的方式一次收發多個封包,大幅降低了單一封包所需的固定成本。雖然這種專核專用的輪詢會讓該 CPU 的運載率呈現 100% 滿載,但它徹底消除了硬體中斷與上下文切換的巨大延遲,因此極度適合高封包率的極致低延遲環境。

2. 記憶體優化的三大關鍵 —— 從萬坪停車場到免排隊的超快得來速

  • Hugepage —— 作業系統的底層土地:DPDK 大量使用 Linux 的 Hugepage 功能,向作業系統要一塊巨大且不間斷的實體記憶體。Linux 預設的記憶體頁面只有 4 KB,而 DPDK 會要求系統開闢 2 MB 甚至 1 GB 的 Hugepage。這可以大幅減少快取失效的次數,減輕 CPU 在管理記憶體虛擬位址轉換時的實體負擔。

  • Mempool —— 蓋好一座停車場:如果每收到一個封包都重新 malloc()free() 配置記憶體,效能肯定會受到影響。因此 DPDK 通常會事先準備大量封包記憶體,需要時直接取出使用。
    ┌─────────────┐
    │⠀mbuf⠀│⠀mbuf⠀│⠀mbuf⠀│
    │⠀mbuf⠀│⠀mbuf⠀│⠀mbuf⠀│
    │⠀mbuf⠀│⠀mbuf⠀│⠀mbuf⠀│
    └─────────────┘
    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀↓
    ⠀⠀⠀⠀⠀⠀⠀⠀網路封包

  • data_off 的妙用 —— 停車格內的指標操作:當封包通過不同網路層時,DPDK 不需要複製記憶體。只需要增加 data_off 的數值並減少 data_len,就能瞬間完成解封包操作。

3. rte_mbuf —— 承載資料的貨櫃

rte_mbuf 是 DPDK 最核心的資料結構,負責封裝與描述所有的網路封包。rte_mbuf 在初始化時從 hugepage 中分配好的固定大小記憶體區塊,運作時只做 allocate 借用與 free 還回,完全避免了 malloc 等動態記憶體配置的效能損耗。

rte_mbuf
⠀├── 封包資訊
⠀├── 長度
⠀├── 狀態
⠀└── 封包資料
⠀⠀⠀⠀⠀├── Ethernet Header
⠀⠀⠀⠀⠀├── IP Header
⠀⠀⠀⠀⠀├── TCP / UDP Header
⠀⠀⠀⠀⠀└── Payload

rte_mbuf 在記憶體中其實分為兩個主要區域,並且是連續儲存的以確保高效率的存取:
┌──────────────────────────────┐
│ rte_mbuf 結構體   // metadata               │
│  ├── buf_addr  // 指向 headroom 的起點        │
│  ├── data_off  // 資料相對於 buf_addr 的偏移量    │
│  ├── pkt_len   // 整個封包的總長度          │
│  ├── data_len  // 當前 mbuf 的資料長度        │
│  └── ol_flags  // offload 狀態標記,例如 checksum │
├──────────────────────────────┤
│ headroom // 預留空間,通常為 128 bytes,用於封裝外層 header│
├──────────────────────────────┤
│ data room // 實際封包資料,即網路通訊協定疊的內容     │
│  ├── ethernet header                 │
│  ├── ip header                    │
│  ├── tcp / udp header                 │
│  └── payload                     │
├──────────────────────────────┤
│ tailroom //尾部預留空間,用於附加額外資料         │
└──────────────────────────────┘

原本每個封包都必須付出的 function call 與 register 讀寫成本,直接被整批 packets 共同分攤。同時,這大幅優化了 l1/l2 cache 的利用率。當系統在處理 rte_mbuf 陣列時,CPU 可以利用 prefetch 指令,將下一批封包的 metadata 提早載入 cache 中。這能讓 CPU 完美避免因 cache miss 造成的狀態閒置,進而將等待記憶體延遲的時間降到最低。

4. DPDK v.s. EF_VI

  • DPDK —— 獨佔模式:把網卡綁定給 DPDK 後,這張網卡在 Linux 系統中就直接消失了,ifconfigip a 都找不到它。所有的封包,不論是開源測試還是系統流量,全部都會倒進 DPDK 程式裡。如果沒有在 DPDK 程式裡寫處理邏輯,系統甚至無法 SSH 連線。因此通常需要伺服器插兩張網卡,一張維運另一張跑 DPDK。

  • EF_VI —— 共享模式:應用程式可以透過配置網卡的 hardware filter,指定僅接收特定條件的流量,例如獲取 UDP Port 5001 的行情資料。網卡硬體會自動將符合該規則的封包直接分流給EF_VI 程式。至於其他的 SSH 遠端連線、作業系統更新等一般流量,則依然維持原狀,安全地透過傳統 linux kernel 的網路疊進行運作。這對維運人員非常友善。

在高頻交易這個對 latency 最敏感的領域,EF_VI 的絕對延遲通常會稍微低一點點,且 jitter 更小。因為它是專為 Solarflare 硬體量身打造的,flow steering 做得極其底層。DPDK 則要照顧各種不同廠商的網卡,框架包裝得比較厚重,記憶體管理也相對複雜。

當然,Kernel Bypass 並不等於完全不需要 Kernel。真正被「繞過」的是 Linux 傳統 Network Stack 及 Socket 資料路徑。DPDK 仍然可能需要 Linux Kernel 幫忙做:

  • CPU 管理
  • 記憶體管理
  • Hugepage 管理
  • PCIe 裝置管理
  • 網卡綁定
  • 啟動與系統管理

當作業系統幫忙處理好上述底層管理後,DPDK 的資料流便能高效運轉:
PMD → RX/TX Queue → rte_mbuf → Mempool → Ring → CPU Core

順帶一提,雖然 DPDK、io_uringAF_XDP、RDMA、SPDK 都常被放在「高效能 I/O」這個大方向裡討論,但它們的 bypass 程度和架構其實不完全相同。例如 AF_XDP 依然保留在核心內執行,而 DPDK 則是徹底的使用者空間化。


上一篇
[Day 22] HFT Networking Infrastructure: Kernel Bypass I
下一篇
[Day 24] HFT Networking Infrastructure: Non-Blocking I/O
系列文
從 C++ 菜鳥到 Low-Latency 勇者:一場分秒必爭的賽局24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言