筆者在很早之前就看eBPF這類似的文章,那時候看這個技術一臉懵逼,不知道它是用來做什么,可以解決什么問題。所以也沒有太關注這個技術。很慶幸最近剛好有機會研究這個技術。
什么是BPF
「BPF的全稱是Berkaley Packet Filter,即伯克利報文過法器,它的設計思想來源于 1992 年Steven McCanne和Van Jacobson寫的一篇論文“The BSD packet filter. A New architecture for user-level packet apture' (《BSD數據包過濾器:一種用于用戶級數據包捕獲的新休系結構》)。最初,BPF是在 BSD 內核實現的,后來,由于其出色的設計思想,其他操作系統也將其引入包括 Linux。」
「在這篇論文中,作者描述了他們如何在Unix內核實現網絡數據包過濾,這種新的技術比當時最先進的數據包過濾技術快20倍。如下圖來源于論文:」
「通俗易懂的理解上圖,BPF是作為網絡報文傳輸的旁路鏈路,當接收到的網絡報文到達內閣驅動程序后,網絡報文在傳輸給網絡協議棧的同時,會額外將網絡報文的副本傳輸給BPF。之后網絡報文會經過BPF程序的內部邏輯進行過濾,最終再送到用戶程序。」
「BPF 在數據包過濾上引入了兩大革新:」
「我們熟悉的tcpdump就是基于BPF技術,好比一個神器站另外一個神器的絕作。」
什么是eBPF
BPF發展到現在名稱升級為eBPF: 「extended Berkeley Packet Filter」。它演進成為了一套通用執行引擎,提供可基于系統或程序事件高效安全執行特定代碼的通用能力,通用能力的使用者不再局限于內核開發者。其使用場景不再僅僅是網絡分析,可以基于eBPF開發性能分析、系統追蹤、網絡優化等多種類型的工具和平臺。
eBPF原理
** eBPF技術架構圖:**
eBPF主要分為用戶空間程序與內核程序兩大部分:
-
在用戶空間,程序通過LLVM/Clang被編譯成eBPF可接受的字節碼并提交到內核,以及負責讀取內核回傳的消息事件或統計信息。eBPF提供了兩種內核態與用戶態傳遞數據的方式,內核態可以將自定義perf_event消息事件發往用戶態,或用戶態通過文件描述符讀寫存儲在內核中的k/v Map數據。
-
在內核空間,為了穩定與安全,eBPF接收的字節碼首先會交給Verifier進行安全驗證,如驗證程序循環次數,數組越界問題,無法訪問的指令等等。只有校驗通過的字節碼才會提交到內核自帶編譯器或JIT編譯器編譯成可直接執行的機器指令。同時,eBPF對提交程序提出限制,如程序大小限制,最大可使用堆棧大小限制,可調用函數限制,循環次數限制等。
-
從上面的架構圖可以看出,eBPF在內核態會依賴內核探針進行工作,其中kprobes實現內核函數動態跟蹤;uprobes實現用戶函數動態跟蹤;tracepoints是內核中的靜態跟蹤點;perf_events支持定時采樣和PMC。
eBPF環境搭建
為了有一個eBPF程序編寫驗證的平臺,我在ubuntu22.04中搭建了eBPF環境,ubuntu22.04安裝流程在這里不在過多的介紹。「以下的操作都在root用戶下執行」
- 更新系統的包索引和包列表:
#aptupdate
#sudoaptinstalllinux-headers-$(uname-r)
- 安裝eBPF依賴工具:
#aptinstall-ybisonflexbuild-essentialgitcmakemakelibelf-devstracetarlibfl-devlibssl-devlibedit-devzlib1g-devpythonpython3-distutils
- 安裝LLVM,并檢查一下版本:
#aptinstallllvm
#llc-version
UbuntuLLVMversion14.0.0
.....
wasm32-WebAssembly32-bit
wasm64-WebAssembly64-bit
x86-32-bitX86:Pentium-Proandabove
x86-64-64-bitX86:EM64TandAMD64
xcore-XCore
#
- 安裝Clang,并檢查一下版本:
#aptinstallclang
#clang-version
Ubuntuclangversion14.0.0-1ubuntu1
Target:x86_64-pc-linux-gnu
Threadmodel:posix
InstalledDir:/usr/bin
#
- 查看但錢ubuntu的內核版本,安裝對應的內核源碼,并解壓源碼:
#apt-cachesearchlinux-source
linux-source-LinuxkernelsourcewithUbuntupatches
linux-source-5.19.0-Linuxkernelsourceforversion5.19.0withUbuntupatches
#aptinstalllinux-source-5.19.0
#cd/usr/src
#tar-jxvflinux-source-5.19.0.tar.bz2
#cdlinux-source-5.19.0
- 編譯內核源碼的bpf模塊,如果沒有報錯,說明已經完成環境搭建:
#cp-v/boot/config-$(uname-r).config
#makeoldconfig&&makeprepare
#makeheaders_install
#apt-getinstalllibcap-dev
#makeM=samples/bpf
CCsamples/bpf/../../tools/testing/selftests/bpf/cgroup_helpers.o
CCsamples/bpf/../../tools/testing/selftests/bpf/trace_helpers.o
CCsamples/bpf/cookie_uid_helper_example.o
CCsamples/bpf/cpustat_user.o
CCsamples/bpf/fds_example.o
....
WARNING:Symbolversiondump"Module.symvers"ismissing.
Modulesmaynothavedependenciesormodversions.
Youmaygetmanyunresolvedsymbolwarnings.
eBPF樣例編寫
在內核源碼的samples/bpf目錄下提供了很多實例供我們學習,通過目錄下的makefile就可以構建里面的bpf程序,如果我們用 C 語言編寫的 BPF 程序編譯可以直接在該目錄提供的環境中進行編譯。
samples/bpf 下的程序一般組成方式是 xxx_user.c 和 xxx_kern.c:
- xxx_user.c:為用戶空間的程序用于設置 BPF 程序的相關配置、加載 BPF 程序至內核、設置 BPF 程序中的 map 值和讀取 BPF 程序運行過程中發送至用戶空間的消息等。目前 xxx_user.c 與 xxx_kern.c 程序在交互實現都是基于 bpf() 系統調用完成的。直接使用 bpf() 系統調用涉及的參數和細節比較多,使用門檻較高,因此為了方便用戶空間程序更加易用,內核提供了 libbpf 庫封裝了對于 bpf() 系統調用的細節。
- xxx_kern.c:為 BPF 程序代碼,通過 clang 編譯成字節碼加載至內核中,在對應事件觸發的時候運行,可以接受用戶空間程序發送的各種數據,并將運行時產生的數據發送至用戶空間程序。
編寫一個樣例流程,在目錄samples/bpf中新建兩個文件:youyeetoo_user.c和youyeetoo_kern.c,并且在makefile中加入構建:
- youyeetoo_user.c的內容:
#include
#include
#include
#include"trace_helpers.h"
intmain(intac,char**argv)
{
structbpf_link*link=NULL;
structbpf_program*prog;
structbpf_object*obj;
charfilename[256];
snprintf(filename,sizeof(filename),"%s_kern.o",argv[0]);
obj=bpf_object__open_file(filename,NULL);
if(libbpf_get_error(obj)){
fprintf(stderr,"ERROR:openingBPFobjectfilefailedn");
return0;
}
prog=bpf_object__find_program_by_name(obj,"bpf_prog");
if(!prog){
fprintf(stderr,"ERROR:findingaproginobjfilefailedn");
gotocleanup;
}
/*loadBPFprogram*/
if(bpf_object__load(obj)){
fprintf(stderr,"ERROR:loadingBPFobjectfilefailedn");
gotocleanup;
}
link=bpf_program__attach(prog);
if(libbpf_get_error(link)){
fprintf(stderr,"ERROR:bpf_program__attachfailedn");
link=NULL;
gotocleanup;
}
read_trace_pipe();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return0;
}
- youyeetoo_kern.c的內容:
#include
#include
#include
#include
SEC("tracepoint/syscalls/sys_enter_execve")
intbpf_prog1(structpt_regs*ctx)
{
charfmt[]="youyeetoo%s!n";
charcomm[16];
bpf_get_current_comm(&comm,sizeof(comm));
bpf_trace_printk(fmt,sizeof(fmt),comm);
return0;
}
char_license[]SEC("license")="GPL";
u32_versionSEC("version")=LINUX_VERSION_CODE;
- Makefile 文件修改:
#diff-uMakefile.oldMakefile
---Makefile.old2021-09-2603:16:16.883348130+0000
+++Makefile2021-09-2603:20:46.732277872+0000
@@-55,6+55,7@@
tprogs-y+=xdp_sample_pkts
tprogs-y+=ibumad
tprogs-y+=hbm
+tprogs-y+=youyeetoo
#Libbpfdependencies
LIBBPF=$(TOOLS_PATH)/lib/bpf/libbpf.a
@@-113,6+114,7@@
xdp_sample_pkts-objs:=xdp_sample_pkts_user.o
ibumad-objs:=ibumad_user.o
hbm-objs:=hbm.o$(CGROUP_HELPERS)
+youyeetoo-objs:=youyeetoo_user.o$(TRACE_HELPERS)
#Tellkbuildtoalwaysbuildtheprograms
always-y:=$(tprogs-y)
@@-174,6+176,7@@
always-y+=hbm_out_kern.o
always-y+=hbm_edt_kern.o
always-y+=xdpsock_kern.o
+always-y+=youyeetoo_kern.o
ifeq($(ARCH),arm)
#Stripallexcept-D__LINUX_ARM_ARCH__optionneededtohandlelinux
eBPF樣例驗證
- 編譯樣例:
#makeM=samples/bpf
CCsamples/bpf/../../tools/testing/selftests/bpf/cgroup_helpers.o
CCsamples/bpf/../../tools/testing/selftests/bpf/trace_helpers.o
CCsamples/bpf/cookie_uid_helper_example.o
CCsamples/bpf/cpustat_user.o
CCsamples/bpf/fds_example.o
....
LDsamples/bpf/youyeetoo
CLANG-bpfsamples/bpf/youyeetoo_kern.o
WARNING:Symbolversiondump"Module.symvers"ismissing.
Modulesmaynothavedependenciesormodversions.
Youmaygetmanyunresolvedsymbolwarnings.
- 在samples/bpf下查看編譯結果,可以看到youyeetoo可執行文件:
#ls-alyouyeetoo*
-rwxr-xr-x1rootroot4079766月919:08youyeetoo
-rw-r--r--1rootroot4516月910:44youyeetoo_kern.c
-rw-r--r--1rootroot52166月919:08youyeetoo_kern.o
-rw-r--r--1rootroot9976月910:40youyeetoo_user.c
-rw-r--r--1rootroot33606月919:08youyeetoo_user.o
- 在ubuntu中運行兩個終端,用來測試youyeetoo:
- 在終端以運行youyeetoo可執行文件,在終端2中執行任意命令,在終端1查看程序是否能夠監測到,如果成功監測到新進程運行便會輸出一條“bpf_trace_printk: Hello”
原文標題:基于ubuntu22.04-深入淺出 eBPF
文章出處:【微信公眾號:Rice 嵌入式開發技術分享】歡迎添加關注!文章轉載請注明出處。
-
UNIX
+關注
關注
0文章
296瀏覽量
41503 -
BPF
+關注
關注
0文章
25瀏覽量
4015
發布評論請先 登錄
相關推薦
評論