作者 | Quentin Monnet
譯者 | qhwdw ? ? ? ? ? 共計翻譯:102 篇 貢獻時間:154 天
~ 更新於[1] 2017-11-02 ~
什麼是 BPF?
BPF,及伯克利包過濾器,最初構想提出於 1992 年,其目的是為了提供一種過濾包的方法,並且要避免從核心空間到使用者空間的無用的資料包複製行為。它最初是由從使用者空間註入到內核的一個簡單的位元組碼構成,它在那個位置利用一個校驗器進行檢查 —— 以避免核心崩潰或者安全問題 —— 並附著到一個套接字上,接著在每個接收到的包上執行。幾年後它被移植到 Linux 上,並且應用於一小部分應用程式上(例如,tcpdump
)。其簡化的語言以及存在於核心中的即時編譯器(JIT),使 BPF 成為一個效能卓越的工具。
然後,在 2013 年,Alexei Starovoitov 對 BPF 進行徹底地改造,並增加了新的功能,改善了它的效能。這個新版本被命名為 eBPF (意思是 “extended BPF”),與此同時,將以前的 BPF 變成 cBPF(意思是 “classic” BPF)。新版本出現瞭如對映和尾呼叫這樣的新特性,並且 JIT 編譯器也被重寫了。新的語言比 cBPF 更接近於原生機器語言。並且,在核心中建立了新的附著點。
感謝那些新的鉤子,eBPF 程式才可以被設計用於各種各樣的情形下,其分為兩個應用領域。其中一個應用領域是核心跟蹤和事件監控。BPF 程式可以被附著到探針(kprobe),而且它與其它跟蹤樣式相比,有很多的優點(有時也有一些缺點)。
另外一個應用領域是網路程式設計。除了套接字過濾器外,eBPF 程式還可以附加到 tc(Linux 流量控制工具)的入站或者出站介面上,以一種很高效的方式去執行各種包處理任務。這種使用方式在這個領域開創了一個新的天地。
並且 eBPF 透過使用為 IO Visor 專案開發的技術,使它的效能進一步得到提升:也為 XDP(“eXpress Data Path”)添加了新的鉤子,XDP 是不久前新增到核心中的一種新式快速路徑。XDP 與 Linux 棧組合,然後使用 BPF ,使包處理的速度更快。
甚至一些專案,如 P4、Open vSwitch,考慮[2] 或者開始去接洽使用 BPF。其它的一些,如 CETH、Cilium,則是完全基於它的。BPF 是如此流行,因此,我們可以預計,不久之後,將圍繞它有更多工具和專案出現 …
深入理解位元組碼
就像我一樣:我的一些工作(包括 BEBA[3])是非常依賴 eBPF 的,並且在這個網站上以後的幾篇文章將關註於這個主題。按理說,在深入到細節之前,我應該以某種方式去介紹 BPF —— 我的意思是,真正的介紹,在第一節所提供的簡要介紹上更多地介紹在 BPF 上開發的新功能:什麼是 BPF 對映?尾呼叫?內部結構是什麼樣子?等等。但是,在這個網站上已經有很多這個主題的介紹了,而且,我也不希望去寫另一篇 “BPF 介紹” 的重覆文章。
畢竟,我花費了很多的時間去閱讀和學習關於 BPF 的知識,因此,在這裡我們將要做什麼呢,我收集了非常多的關於 BPF 的閱讀材料:介紹、檔案,也有教程或者示例。這裡有很多的材料可以去閱讀,但是,為了去閱讀它,首先要去 找到 它。因此,為了能夠幫助更多想去學習和使用 BPF 的人,現在的這篇文章給出了一個資源清單。這裡有各種閱讀材料,它可以幫你深入理解核心位元組碼的機制。
資源
簡介
這篇文章中下麵的連結提供了 BPF 的基本概述,或者,一些與它密切相關的一些主題。如果你對 BPF 非常陌生,你可以在這些介紹文章中挑選出一篇你喜歡的文章去閱讀。如果你已經理解了 BPF,你可以針對特定的主題去閱讀,下麵是閱讀清單。
關於 BPF
關於 eBPF 的常規介紹:
一篇寫的很好的,並且易於理解的,介紹 eBPF 子系統元件的概述文章。
快速理解所有的關於 eBPF 和 XDP 的基礎概念的最好講稿中的一篇(主要是關於網路處理的)
一篇非常好的介紹文章,主要是關於跟蹤方面的。
主要內容是跟蹤使用案例相關的。
第一部分是關於火焰圖的使用。
介紹了 IO Visor 專案。
eBPF 的作者寫的一篇講稿。
BPF 內部結構:
Daniel 介紹了 eBPF 的細節,及其用於隧道和封裝、直接包訪問和其它特性。
介紹 eBPF 之後,它提供了許多 BPF 內部機制(對映管理、尾呼叫、校驗器)的見解。對於大多數有志於 BPF 的人來說,這是必讀的!全文在這裡[18]。
這些介紹可能是理解 eBPF 內部機制設計與實現的最佳檔案資源之一。
IO Visor 部落格[21] 有一些關於 BPF 的值得關註技術文章。它們中的一些包含了一點營銷討論。
核心跟蹤:總結了所有的已有的方法,包括 BPF:
Kprobes、uprobes、ftrace
Systemtap、Kernelshark、trace-cmd、LTTng、perf-tool、ftrace、hist-trigger、perf、function tracer、tracepoint、kprobe/uprobe …
關於 事件跟蹤和監視,Brendan Gregg 大量使用了 eBPF,並且就其使用 eBPFR 的一些案例寫了極好的檔案。如果你正在做一些核心跟蹤方面的工作,你應該去看一下他的關於 eBPF 和火焰圖相關的部落格文章。其中的大多數都可以 從這篇文章中[24] 訪問,或者瀏覽他的部落格。
介紹 BPF,也介紹 Linux 網路的一般概念:
硬體解除安裝(LCTT 譯註:“解除安裝”是指原本由軟體來處理的一些操作交由硬體來完成,以提升吞吐量,降低 CPU 負荷。):
綜合 XDP 解除安裝——處理邊界案例[28](Jakub Kicinski 和 Nic Viljoen,netdev 2.2 ,Seoul,November 2017)
XDP 硬體解除安裝的挑戰[29](Quentin Monnet,FOSDEM 2018,Brussels,February 2018)
關於 cBPF:
它是關於(經典)BPF 的最早的論文。
xt_bpf
模組一起使用 BPF 位元組碼[34]。值得一提的是,從 Linux 核心 4.10 開始,eBPF 也是透過這個模組支援的。(雖然,我並不知道關於這件事的任何討論或者文章)關於 XDP
這是第一個關於 XDP 的演講。
包含一些(有點營銷的意思?)基準測試結果!使用單一核心:
(測試是用 mlx4 驅動程式執行的)。
“Linux 核心與 DPDK 的鬥爭” 。未來的計劃(在寫這篇文章時)它用 XDP 和 DPDK 進行比較。
關於 XDP 內部結構和預期演化的附加提示。
包含了關於 XDP 的詳細情況和使用案例,以及 效能測試 的 效能測試結果 和 程式碼片斷,以及使用 eBPF/XDP(基於一個 IP 黑名單樣式)的用於 基本的 DDoS 防護。
提供了許多 XDP 開發者當前所面對 記憶體問題 的許多細節。不要從這一個開始,但如果你已經理解了 XDP,並且想去瞭解它在頁面分配方面的真實工作方式,這是一個非常有用的資源。
普通人怎麼開始使用 eBPF 和 XDP。這個演講也由 Julia Evans 在 她的部落格[45]上做了總結。
該演講的修訂版本,包含了新的內容。
(Jesper 也建立了並且嘗試去擴充套件了有關 eBPF 和 XDP 的一些檔案,檢視 相關節[47]。)
在這篇文章中,只有影片可用,我不知道是否有幻燈片。
在 Linux 上的最先進的包過濾的介紹,面向 DDoS 的保護、討論了關於在核心中進行包處理、核心旁通、XDP 和 eBPF。
關於 基於 eBPF 或者 eBPF 相關的其它元件
提出了使用 P4,一個包處理的描述語言,使用 BPF 去建立一個高效能的可程式設計交換機。
另一個 P4 的演講,一些有關於 Netronome 的 NFP(網路流處理器)架構上的 eBPF 硬體解除安裝的因素。
在上述不同的演講中重覆了大量的內容;嫌麻煩就選最近的一個。Daniel Borkmann 作為 Google 開源部落格的特邀作者,也寫了 Cilium 簡介[59]。
據我所知,這些 eBPF 的使用案例看上去僅處於提議階段(並沒有合併到 OvS 的主分支中),但是,看它帶來了什麼將是非常值得關註的事情。
CETH,是由 Mellanox 發起的,為實現更快的網路 I/O 而主張的通用乙太網驅動程式架構。
當使用原生驅動的 XDP 時,這個專案要求實現非常高的效能。
InKeV 是一個基於 eBPF 的虛擬網路、標的資料中心網路的資料路徑架構。它最初由 PLUMgrid 提出,並且聲稱相比基於 OvS 的 OpenStack 解決方案可以獲得更好的效能。
“一個來自 Go 庫,可以去建立、載入和使用 eBPF 程式”
檔案
一旦你對 BPF 是做什麼的有一個大體的理解。你可以拋開一般的演講而深入到檔案中了。下麵是 BPF 的規範和功能的最全面的檔案,按你的需要挑一個開始閱讀吧!
關於 BPF
bpf()
系統呼叫,它用於從使用者空間去管理 BPF 程式和對映。它也包含一個 BPF 高階特性的描述(程式型別、對映等等)。第二個是主要是處理希望附加到 tc 介面的 BPF 程式:它是 tc-bpf(8) man 頁面[86],是 使用 BPF 和 tc 的一個參考,並且包含一些示例命令和參考程式碼。最後一個可能是目前來說關於校驗器的最佳的總結。
關於 tc
當為了網路目的結合使用 BPF 與 tc (Linux 流量控制工具)時,它可用於收集 tc 的常規功能的資訊。這裡有幾個關於它的資源。
註意: 這些檔案在 2017 年 10 月 已經從 iproute2 中刪除,然而,從 Git 歷史中卻一直可用。
tc
較多,這裡有一些好訊息:我用這個工具 寫了一個 bash 補完功能[100],並且它被包 iproute2 帶到核心版本 4.6 和更高版中!關於 XDP
關於 P4 和 BPF
P4[103] 是一個用於指定交換機行為的語言。它可以為多種標的硬體或軟體編譯。因此,你可能猜到了,這些標的中的一個就是 BPF … 僅部分支援的:一些 P4 特性並不能被轉化到 BPF 中,並且,用類似的方法,BPF 可以做的事情,而使用 P4 卻不能表達出現。不過,P4 與 BPF 使用 的相關檔案,被隱藏在 bcc 倉庫中[104]。這個改變在 P4_16 版本中,p4c 取用的編輯器包含 一個 eBPF 後端[105]。
教程
Brendan Gregg 為想要 使用 bcc 工具 跟蹤和監視核心中的事件的人製作了一個非常好的 教程。第一個教程是關於如何使用 bcc 工具[84],它有許多章節,可以教你去理解怎麼去使用已有的工具,而 針對 Python 開發者的一篇[106] 專註於開發新工具,它總共有十七節 “課程”。
Sasha Goldshtein 也有一些 Linux 跟蹤研究材料[107] 涉及到使用幾個 BPF 工具進行跟蹤。
Jean-Tiare Le Bigot 的另一篇文章提供了一個詳細的(和有指導意義的)使用 perf 和 eBPF 去設定一個低階的跟蹤器[108] 的示例。
對於網路相關的 eBPF 使用案例也有幾個教程。有一些值得關註的檔案,包括一篇 eBPF 解除安裝入門指南,是關於在 Open NFP[109] 平臺上用 Netronome 操作的。其它的那些,來自 Jesper 的演講,XDP 能為其它人做什麼[44](及其第二版[46]),可能是 XDP 入門的最好的方法之一。
示例
有示例是非常好的。看看它們是如何工作的。但是 BPF 程式示例是分散在幾個專案中的,因此,我列出了我所知道的所有的示例。示例並不是總是使用相同的 helper(例如,tc 和 bcc 都有一套它們自己的 helper,使它可以很容易地去用 C 語言寫 BPF 程式)
來自內核的示例
核心中包含了大多數型別的程式:過濾器系結到套接字或者 tc 介面、事件跟蹤/監視、甚至是 XDP。你可以在 linux/samples/bpf/[110] 目錄中找到這些示例。
現在,更多的示例已經作為單元測試被新增到 linux/tools/testing/selftests/bpf[111] 目錄下,這裡麵包含對硬體解除安裝的測試或者對於 libbpf 的測試。
Jesper 的 Dangaard Brouer 在他的 prototype-kernel[112] 倉庫中也維護了一套專門的示例。 這些示例與那些核心中提供的示例非常類似,但是它們可以脫離核心架構(Makefile 和頭檔案)編譯。
也不要忘記去看一下 git 相關的提交歷史,它們介紹了一些特定的特性,也許包含了一些特性的詳細示例。
來自包 iproute2 的示例
iproute2 包也提供了幾個示例。它們都很明顯地偏向網路程式設計,因此,這個程式是附著到 tc 入站或者出站介面上。這些示例在 iproute2/examples/bpf/[113] 目錄中。
來自 bcc 工具集的示例
許多示例都是 與 bcc 一起提供的[114]:
tracing
目錄包含許多 跟蹤程式設計 的示例。前面的教程中提到的都在那裡。那些程式涉及了很大部分的事件監視功能,並且,它們中的一些是面向生產系統的。註意,某些 Linux 發行版(至少是 Debian、Ubuntu、Fedora、Arch Linux)、這些程式已經被 打包了[115] 並且可以很 “容易地” 透過比如 # apt install bcc-tools
進行安裝。但是在寫這篇文章的時候(除了 Arch Linux),首先要求安裝 IO Visor 的包倉庫。手冊頁面
雖然 bcc 一般很容易在核心中去註入和執行一個 BPF 程式,將程式附著到 tc 介面也能透過 tc
工具自己完成。因此,如果你打算將 BPF 與 tc 一起使用,你可以在 tc-bpf(8)
手冊頁面[86] 中找到一些呼叫示例。
程式碼
有時候,BPF 檔案或者示例並不夠,而且你只想在你喜歡的文字編輯器(它當然應該是 Vim)中去顯示程式碼並去閱讀它。或者,你可能想深入到程式碼中去做一個補丁程式或者為機器增加一些新特性。因此,這裡對有關的檔案的幾個建議,找到你想要的函式只取決於你自己!
在核心中的 BPF 程式碼
syscall.c
中實現,而 core.c
包含了 解析器。其它檔案的命名顯而易見:verifier.c
包含 校驗器(不是開玩笑的),arraymap.c
的程式碼用於與陣列型別的 對映 互動,等等。act_bpf.c
(action)和 cls_bpf.c
(filter)中。XDP 鉤子程式碼
一旦裝載進內核的 BPF 虛擬機器,由一個 Netlink 命令將 XDP 程式從使用者空間鉤入到核心網路路徑中。接收它的是在 linux/net/core/dev.c[129] 檔案中的 dev_change_xdp_fd()
函式,它被呼叫並設定一個 XDP 鉤子。鉤子被放在支援的網絡卡的驅動程式中。例如,用於 Netronome 硬體鉤子的 ntp 驅動程式實現放在 drivers/net/ethernet/netronome/nfp/[130]中。檔案 nfp_net_common.c
接受 Netlink 命令,並呼叫 nfp_net_xdp_setup()
,它會轉而呼叫 nfp_net_xdp_setup_drv()
實體來安裝該程式。
在 bcc 中的 BPF 邏輯
在 bcc 的 GitHub 倉庫[131] 能找到的 bcc 工具集的程式碼。其 Python 程式碼,包含在 BPF
類中,最初它在檔案 bcc/src/python/bcc/__init__.py[132] 中。但是許多我覺得有意思的東西,比如,載入 BPF 程式到核心中,出現在 libbcc 的 C 庫[133]中。
使用 tc 去管理 BPF 的程式碼
當然,這些程式碼與 iproute2 包中的 tc 中的 BPF 相關。其中的一些在 iproute2/tc/[134] 目錄中。檔案 f_bpf.c
和 m_bpf.c
(和 e_bpf.c
)各自用於處理 BPF 的過濾器和動作的(和 tc exec
命令,等等)。檔案 q_clsact.c
定義了為 BPF 特別建立的 clsact
qdisc。但是,大多數的 BPF 使用者空間邏輯 是在 iproute2/lib/bpf.c[135] 庫中實現的,因此,如果你想去使用 BPF 和 tc,這裡可能是會將你搞混亂的地方(它是從檔案 iproute2/tc/tc_bpf.c 中移動而來的,你也可以在舊版本的包中找到相同的程式碼)。
BPF 實用工具
核心中也帶有 BPF 相關的三個工具的原始碼(bpf_asm.c
、 bpf_dbg.c
、 bpf_jit_disasm.c
),根據你的版本不同,在 linux/tools/net/[136] (直到 Linux 4.14)或者 linux/tools/bpf/[137] 目錄下麵:
bpf_asm
是一個極小的 cBPF 彙編程式。bpf_dbg
是一個很小的 cBPF 程式除錯器。bpf_jit_disasm
對於兩種 BPF 都是通用的,並且對於 JIT 除錯來說非常有用。bpftool
是由 Jakub Kicinski 寫的通用工具,它可以與 eBPF 程式互動並從使用者空間的對映,例如,去展示、轉儲、pin 程式、或者去展示、建立、pin、更新、刪除對映。閱讀在源檔案頂部的註釋可以得到一個它們使用方法的概述。
與 eBPF 一起工作的其它必需的檔案是來自核心樹的兩個使用者空間庫,它們可以用於管理 eBPF 程式或者對映來自外部的程式。這個函式可以透過 linux/tools/lib/bpf/[138] 目錄中的頭檔案 bpf.h
和 libbpf.h
(更高層面封裝)來訪問。比如,工具 bpftool
主要依賴這些庫。
其它值得關註的部分
如果你對關於 BPF 的不常見的語言的使用感興趣,bcc 包含 一個 BPF 標的的 P4 編譯器[139]以及 一個 Lua 前端[140],它可以被用以代替 C 的一個子集,並且(用 Lua )替代 Python 工具。
LLVM 後端
這個 BPF 後端用於 clang / LLVM 將 C 編譯到 eBPF ,是在 這個提交[141] 中新增到 LLVM 原始碼的(也可以在 這個 GitHub 映象[142] 上訪問)。
在使用者空間中執行
到目前為止,我知道那裡有至少兩種 eBPF 使用者空間實現。第一個是 uBPF[143],它是用 C 寫的。它包含一個解析器、一個 x86_64 架構的 JIT 編譯器、一個彙編器和一個反彙編器。
uBPF 的程式碼似乎被重用來產生了一個 通用實現[144],其聲稱支援 FreeBSD 核心、FreeBSD 使用者空間、Linux 核心、Linux 使用者空間和 Mac OSX 使用者空間。它被 VALE 交換機的 BPF 擴充套件模組[69]使用。
其它使用者空間的實現是我做的:rbpf[145],基於 uBPF,但是用 Rust 寫的。寫瞭解析器和 JIT 編譯器 (Linux 下兩個都有,Mac OSX 和 Windows 下僅有解析器),以後可能會有更多。
提交日誌
正如前面所說的,如果你希望得到更多的關於一些特定的 BPF 特性的資訊,不要猶豫,去看一些提交日誌。你可以在許多地方搜尋日誌,比如,在 git.kernel.org[146]、在 GitHub 上[147]、或者如果你克隆過它還有你的本地倉庫中。如果你不熟悉 git,你可以嘗試像這些去做 git blame
去看看介紹特定程式碼行的提交內容,然後,git show
去看詳細情況(或者在 git log
的結果中按關鍵字搜尋,但是這樣做通常比較單調乏味)也可以看在 bcc 倉庫中的 按核心版本區分的 eBPF 特性串列[94],它連結到相關的提交上。
排錯
對 eBPF 的追捧是最近的事情,因此,到目前為止我還找不到許多關於怎麼去排錯的資源。所以這裡只有幾個,是我在使用 BPF 進行工作的時候,對自己遇到的問題進行的記錄。
編譯時的錯誤
tc-bpf
(用於去編譯 C 程式碼到 BPF 中)的 man 頁面提供的 bcc
shell 函式時:我曾經必須新增包含 clang 呼叫的頭檔案:
__bcc() {
clang -O2 -I "/usr/src/linux-essay-headers-$(uname -r)/include/" \
-I "/usr/src/linux-essay-headers-$(uname -r)/arch/x86/include/" \
-emit-llvm -c $1 -o - | \
llc -march=bpf -filetype=obj -o "`basename $1 .c`.o"
}
(現在似乎修複了)。
bcc
的其它問題,不要忘了去看一看這個工具集的 答疑[148]。在載入和執行時的錯誤
tc
去載入一個程式,確保你使用了一個與使用中的核心版本等價的 iproute2 中的 tc
二進位制檔案。bcc
去載入一個程式,確保在你的系統上安裝了 bcc(僅下載原始碼去執行 Python 指令碼是不夠的)。tc
,如果 BPF 程式不能傳回一個預期值,檢查呼叫它的方式:過濾器,或者動作,或者使用 “直傳” 樣式的過濾器。tc
,註意不使用過濾器,動作不會直接附著到 qdiscs 或者介面。tc
工具有一個 verbose
樣式,它與 BPF 一起工作的很好:在你的命令列尾部嘗試追加一個 verbose
。bcc
也有一個 verbose
選項:BPF
類有一個 debug
引數,它可以帶 DEBUG_LLVM_IR
、DEBUG_BPF
和 DEBUG_PREPROCESSOR
三個標誌中任何組合(詳細情況在 源檔案[132]中)。 為除錯該程式碼,它甚至嵌入了 一些條件去列印輸出程式碼[149]。-g
標誌允許你透過核心校驗器去以人類可讀的格式去轉儲你的程式。處理轉儲檔案,使用:
$ llvm-objdump -S -no-show-raw-insn bpf_program.o
更多!
請經常會回到這篇部落格[159]中,來看一看 關於 BPF[160] 有沒有新的文章!
特別感謝 Daniel Borkmann 指引我找到了 更多的檔案[161],因此我才完成了這個合集。
via: https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/
作者:Quentin Monnet[162] 譯者:qhwdw 校對:wxy
本文由 LCTT 原創編譯,Linux中國 榮譽推出