The bpf() system call

從 2005 年 dtrace 發表後, Linux 陣營也一直在努力加上像 dtrace 一樣功能強大, 高率又安全的 tracing 機制. 但, 為什麼不直接將 dtrace porting 過來? 主要是 license 不兼容的關係 - dtrace 是 CDDL, 而 Linux kernel 是 GPLv2, 所以...

Linux kernel 4.2 時加入了 bpf() 這個 syscall, 讓 Linux kernel 上的 tracing 機制能夠更安全更快速.

BPF 是 Berkeley Packet Filter 的簡稱, 在 kernel 中以 VM 執行 BPF instruction, 用作 network packet 的過濾, 後來還加上了 JIT compiler 讓 BPF VM 效率更高 (在 Linux 上, JIT compiler 預設是關閉的).

雖然, bpf() 是在 4.2 時才加上的, 這期間 Linux 社區也不是白白的度過的. 各種不同的 tracing 工具不斷嘗試著想達到這個目標, 如 systemtap, ktap. 在 kernel 中, 也實現了如 static tracepoint 的靜態事件源, 配合 compiler 產生的 mcount() 調用追蹤絕大部份的 kernel function, kprobe 及 uprobe 實現了按需動態增加 probe point 的機制, 以及跟 CPU 相關的 perf_events. 到目前為止, 基本可用性及穩定性相當高了, 但缺少了一個在 user space 能很好的使用這些機制的工具及方法.

考量到 tracing 本身的複雜度 (需求的多變), 事件的發生頻率 (影響資料量), 如果能有個機制讓 event handler 運行於 kernel 中 (降低了 context switch 或是 buffer overflow 問題), 過濾, 統計後, 將結果發還 user space (減少 kernel <=> user space 間的資料傳輸量), 再配合 user space 的工具產生出這些 event handler 後 inject 到 kernel 中, 並且要在設計上是能被驗証確認不會造成如 infinite loop, kernel panic 等問題, 這樣才能真正安全可靠的被用在 production environment.

bpf() syscall 就是這樣機制的核心. 通過這個 syscall, 你可以在 kernel 中配置 array, hash table 用作資料的儲存, 在需要時由 user space 主動取回.

bpf() syscall 也可以用來載入一段擴充過的 BPF instruction 集合 (eBPF, extended BPF, 不過現在統一叫作 BPF 了) 到 kernel 中執行. 蒐集到的資訊就放在預先配置好的 array or hashtable 中, 因此非常高效 (如果開啟 JIT compiler 的話效率會再高出不少). 並且 BFP VM 及相關機制是一致的, 非按需產生新的 kernel module (如 systemtap 的做法), 在每個 kernel 版本中不斷的被修正及優化, 不斷的提高效能及可靠度.

之後會陸續發出相關使用介紹及進展, 讓我們一起期待!