可觀測性與追蹤

BPF maps(BPF 對映表)

一支 eBPF 程式在核心深處執行,每次鉤點觸發時短暫地跑一下,然後就消失了——它沒有屬於自己的永久地方來保存累計值,而想要結果的那個使用者空間工具,又住在一個完全分開的世界裡。BPF map 就是橋接兩者的共享檔案櫃:一個存在核心裡的鍵值資料結構,能在程式的多次觸發之間存續,並且能被「核心端的 eBPF 程式」與「使用者空間行程」兩邊讀寫。

具體來說,建立一個 BPF map 時要選定型別、鍵的大小、值的大小。為不同工作調校過的 map 型別很多:雜湊表(用任意鍵查值,例如以 PID 為鍵的每行程計數器)、陣列(以整數索引的快速槽位)、每 CPU 變體(每個 CPU 各有一份副本,因此並行更新不需要鎖,事後由使用者空間相加)、環形緩衝區或 perf 緩衝區(把事件串流到使用者空間的佇列)、以及堆疊追蹤 map(儲存擷取到的呼叫堆疊)。eBPF 程式在事件發生時更新 map——遞增計數器、附加事件——用驗證器會檢查的核可輔助呼叫。另一邊,使用者空間工具透過 bpf() 系統呼叫週期性地讀取 map,以收集並顯示結果。map 是 eBPF 程式保存狀態、並把資料交回外面的「唯一」獲准管道。

它之所以重要,是因為它正是把一支稍縱即逝的核心內程式變成有用工具的機制:程式在核心裡做便宜的每事件工作(計數、分桶、取樣),map 把彙總後的答案帶到使用者空間,而不是把每一筆原始事件都運出去(那會貴得多)。誠實的提醒:map 有事先宣告、固定預設大小的容量,所以雜湊表可能填滿並開始丟棄新鍵;每 CPU 型別以「無法在每一瞬間都讀到精確值」換取無鎖速度(使用者空間必須跨 CPU 相加);而環形緩衝區在洪峰下可能溢位並丟掉事件,所以工具該誠實面對可能的遺漏,而不是假設它看到了全部。

BPF_HASH(counts, u32, u64); // 鍵 = PID (u32),值 = 計數 (u64),核心端 // 每個事件時: counts[pid] += 1; // 使用者空間稍後: for k,v in counts: print(k, v) # 透過 bpf() 系統呼叫讀取

一個以 PID 為鍵的雜湊 map 在核心裡累積計數;使用者空間透過 bpf() 系統呼叫讀取做好的累計值。

一個 map 的容量在建立時就固定了,所以在重負載下雜湊 map 可能撞到大小上限、悄悄地插不進新鍵——一個從滿了的 map 回報的工具可能正在不聲不響地少算。

又称
eBPF mapsBPF mapBPF 映射BPF 對映