《聞茂泉--基于libbpf庫的ebpf構建方案_第二屆eBPF開發者大會報告.pdf》由會員分享,可在線閱讀,更多相關《聞茂泉--基于libbpf庫的ebpf構建方案_第二屆eBPF開發者大會報告.pdf(32頁珍藏版)》請在三個皮匠報告上搜索。
1、基于libbpf庫的eBPF編程方案第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m中 國 西 安主講人:聞茂泉2024-04-13目錄Content01eBPF編程方案簡介02基于libbpf的編程方案03改進的libbpf編程方案04低版本內核編程方案05低版本編譯環境編程方案第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m01eBPF編程方案簡介第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m中 國 西 安主流的C語言實現的eBPF編程方案代際方案指稱識別方法備注第1代
2、bpf_load.c文件方案代碼中有bpf_load.c文件,還有load_bpf_file函數。Linux 4.x 系列早期內核版本的源碼實例大多基于此文件,這個舊 API 方案已經在內核中被逐步廢棄。第2代原生libbpf庫方案代碼中有libbpf.c文件Linux 5.x版本內核的源碼實例很多使用以libbpf.c為核心的原生libbpf庫方案,是本文重點闡述的方案。第3代libbpf-bootstrap骨架方案代碼中除了libbpf.c文件,還有libbpf-bootstrap、skeleton和*.skel.h關鍵詞最新版本內核的源碼實例已經開始采用此方案。業界最新的eBPF介紹文章
3、較多基于此方案。1.更深的控制和靈活性:直接使用原生libbpf 庫的方案意味著可以與更底層交互,實現更多的控制,定制加載和管理 eBPF 程序和 maps 過程,滿足更復雜的需求。2.更好的學習和理解:libbpf-bootstrap封裝抽象屏蔽了很多細節,直接使用原生libbpf可以對 eBPF 子系統有更深入的理解,有利于開發者對 eBPF 內部工作原理的理解。3.更細粒度的依賴管理:直接使用原生libbpf庫能夠指定依賴的 libbpf 庫版本和功能,進而更精細化地管理項目依賴關系。4.更好的低版本內核適應性:基于原生libbpf庫的方案,在低版本操作系統發行版和低版本內核上可以有更好
4、的兼容性。原生libbpf庫eBPF編程方案的一些獨特優勢:最大硬傷:寫死C語言eBPF編程的基礎環境準備rpm包基礎環境初始化deb包基礎環境初始化推薦發行版Anolis 8.8、CentOS 8.5、Kylin V10、Fedora 33及以上Debian 12、Ubuntu 21.04及以上編譯工具和依賴庫包yum install clang llvm elfutils-libelf-develapt-get updateapt install clang llvm libelf-dev基礎包yum install git makeapt install git make用戶態頭文件yu
5、m install kernel-headers-$(uname-r)apt install linux-libc-devbpftool工具yum install bpftool-$(uname-r)apt install linux-tools-common linux-tools-$(uname-r)主流的linux發行版大多是基于rpm包或deb包的包管理系統。不同的包管理系統,初始化eBPF開發環境時所依賴的包,也略有差別。C語言eBPF編程方案的程序架構execve.cexecve.bpf.c (execve_kern.c)execve.cexecve.bpf.cexecve.bpf
6、.oexecve.skel.hexecveexecve.bpf.o (execve_kern.o)execveexecve.bpf.o (execve_kern.o)./execve./execve第1代,第2代編程方案第3代編程方案編譯過程執行過程實時讀取02基于libbpf的編程方案第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m中 國 西 安Talk is cheap.Show me the code.trace_execve_libbpf130是一個基于libbpf庫的第2代eBPF構建實例。eBPF初學者,可以考慮選擇跟蹤 execve 系統調
7、用產生的事件。執行編譯結果trace_execve命令,完美驗證通過。eBPF項目的目錄結構解析項目目錄說明./項目用戶態代碼和主Makefile./progs項目內核態bpf程序代碼./include項目的業務代碼相關的頭文件./helpers來自于libbpf庫之外的helpler文件./tools/lib/bpf/除Makefile外,來自于libbpf-1.3.0/src/./tools/include/所有都來自于libbpf-1.3.0/include/./tools/build/項目構建時一些feature探測代碼./tools/scripts/項目Makefile所依賴的一些功
8、能函數在這個項目中添加ebpf的代碼,可以遵循這樣的目錄結構。用戶態加載文件放到根目錄下,內核態bpf文件放到progs目錄下,用戶態和內核態公共的頭文件放到include目錄下。eBPF項目的Makefile解析trace_execve_libbpf130項目有4個Makefile,分別如下:1)./Makefile是主文件,用于生成用戶態eBPF程序trace_execve。2)./progs/Makefile 用于生成內核態BPF程序trace_execve.bpf.o。3)./tools/lib/bpf/Makefile 用于生成libbpf.a靜態庫。4)./tools/build/
9、feature/Makefile 用于一些feature的探測。在項目空間的根目錄運行make命令進行項目構建時,會首先執行Makefile文件。在Makefile文件中會通過make的-C選項間接觸發progs/Makefile和tools/lib/bpf/Makefile的執行。內核態bpf程序編譯參數解析編譯參數參數解析-I././tools/lib/由bpftool工具編譯生成的vmlinux.h文件,包含了絕大多數bpf程序的內核態和用戶態依賴,通過此選項可在./tools/lib/目錄搜索到vmlinux.h頭文件。還可通過此選項在./tools/lib/目錄下的bpf子目錄中查找
10、到bpf_helpers.h和bpf_tracing.h頭文件,它們都是對vmlinux.h頭文件內核態依賴的補充。-I././tools/include/uapi/通過此選項,可在./tools/include/uapi/目錄下的linux子目錄中查找到bpf.h頭文件。同時kernel-headers包引入的/usr/include/linux/目錄下也有bpf.h,./tools/include/uapi下的bpf.h優先級會覆蓋它。在一些簡單使用場景,可以使用替代。-idirafter/usr/include/x86_64-linux-gnuclang-target bpf時,標準系統
11、目錄中不包含/usr/include/x86_64-linux-gnu路徑。-iquote././include/通過此編譯選項,可以在./include/目錄中查找到trace_execve.h和common.h頭文件。-D_KERNEL_-D_BPF_TRACING_-D_TARGET_ARCH_x86以上頭文件依賴的預處理過程中,會依賴宏變量來決定預處理的展開邏輯,此處編譯命令中的宏就是起這些作用。比如在bpf_tracing.h頭文件中,就有#if defined(_TARGET_ARCH_x86)的宏判斷語句,來決定預處理展開邏輯走x86分支。-g用于生成execve.bpf.o目標
12、文件中的.BTF和.BTF.ext節。(與低版本環境區別)-target bpf指示Clang將代碼生成為針對eBPF目標的目標代碼。(與低版本環境區別)用戶態加載程序編譯參數解析編譯參數參數解析-iquote./include/通過此選項,可在./include/目錄中查找到trace_execve.h和common.h頭文件。-I./tools/lib/通過此選項,可在./tools/lib/目錄下的bpf子目錄中查找到頭文件。一些老代碼中,有頭文件使用用法,最新的ebpf項目實例,都會將libbpf庫的libbpf.h等頭文件放到bpf子目錄下,推薦統一使用用法。-I./tools/in
13、clude/通過此選項,可在./tools/include/目錄下的linux子目錄中查找到頭文件-I./tools/include/uapi同樣是對kernel-headers包引入的/usr/include/linux/目錄下頭文件的更新升級。標準系統目錄(Standard system directories)除以上頭文件外的其他頭文件,均可以在kerne-headers包提供的標準系統目錄(Standard system directories)的/usr/include/目錄及子目錄中查找到。因此,最終會在/usr/include/linux/perf_event.h位置被查找到。這
14、里同樣是形式頭文件,和卻在兩個完全不同的搜索路徑查被查找到。一個系統上的標準系統目錄如圖示。libbpf.a靜態庫編譯參數解析本項目針對./tools/lib/bpf目錄中的*.c的libbpf庫文件,進行了靜態編譯,最終生成libbpf.a靜態庫文件。之后把libbpf.a靜態庫文件編譯進了所有ebpf應用程序中。在本項目中,完全實現了libbpf庫的自主可控編譯構建過程。這給我們帶來如下兩方面好處:1)對于一些ebpf的資深人士,可以自主修改libbpf庫,實現滿足自己業務需求的優化。2)對于一些ebpf的初學者,完全可以在libbpf庫中任意位置插入printf,學習libbpf庫的原理
15、。03改進的libbpf編程方案第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m中 國 西 安咱們中國人的原創傳統的第2代eBPF編程方案美中不足從實驗結果可以看出,當我們把bpf目標文件trace_execve.bpf.o刪除后,trace_execve程序執行會報錯,提示讀取trace_execve.bpf.o文件不存在。這說明,當前方案構建后,需要將trace_execve程序和bpf目標文件trace_execve.bpf.o這一組文件一起進行分發,才能正常執行。這給我們在工程的實現上帶來了很大的挑戰。為了解決上面提到的問題,第 3 代 ebpf
16、 編程方案 libbpf-bootstrap框架發明了skeleton骨架,即使用*.skel.h頭文件的方式,將bpf目標文件trace_execve.bpf.o的內容編譯進trace_execve程序。這樣后續只需分發trace_execve二進制程序文件即可。關鍵突破:使用hexdump生成skel.h頭文件步驟libbpf-bootstrap框架構建方法可改進機會點1bpftool btf dump file vmlinux format c vmlinux.h2clang-O2-target bpf-c trace_execve.bpf.c-g-o trace_execve.bpf.
17、o3bpftool gen skeleton trace_execve.bpf.o trace_execve.skel.h此步驟用hexdump替換bpftool4gcc-o trace_execve trace_execve.c-lbpf-lelf此步驟更改加載函數為libbpf標準函數libbpf-bootstrap編程框架的第3步依靠bpftool工具將trace_execve.bpf.o這個目標文件轉換成十六進制格式的文本,并將文本內容作為trace_execve.skel.h頭文件中的一個變量的值,最后還需要讓trace_execve.c用戶態加載文件包含這個trace_execve
18、.skel.h頭文件。此步驟,我們可以用hexdump命令將bpf目標文件轉換成十六進制文本并繼續生成skel.h頭文件。有了使用hexdump命令成功頭文件的基礎,我們也可以使用libbpf庫中的原生庫函數完成bpf程序的用戶態加載過程。原來加載函數trace_execve_bpf_load()必須包含程序的關鍵詞trace_execve,但現在可以使用純凈的bpf_object_load()原生庫函數完成同樣的任務了。Talk is cheap.Show me the code once again.從運行結果看,雖然刪除了bpf目標文件trace_execve.bpf.o,僅僅依靠tra
19、ce_execve文件即可成功執行。也可以再嘗試將trace_execve可執行文件拷貝到其他目錄,結果依然可行。改進的eBPF項目Makefile解析主Makefile中,為了實現目標依賴,我們連用了5個靜態模式規則(Static Pattern Rules)。任何一個靜態模式規則的目標集合,都是通過項目根目錄下*.c文件的集合,進行局部字符串替換獲得。用戶態可執行加載程序的主要依賴鏈條如圖。從file到memory實現讀取elf的轉變libbpf庫提供了bpf_object_open_file和bpf_object_open_mem兩個函數用于讀取elf格式的bpf目標文件trace_ex
20、ecve.bpf.o。其中bpf_object_open_file是在trace_execve運行時,去讀取trace_execve.bpf.o文件內容,而bpf_object_open_mem是在編譯時,已經把elf內容編譯進trace_execve程序。這兩個libbpf庫函數,最終都是調用libbpf.c文件中的函數bpf_object_elf_init。在這個函數中,讀取文件的場景會觸發elf標準庫函數elf_begin,而讀取內存的場景會觸發elf標準庫函數elf_memory。改進libbpf編程方案與行業主流方案比較比較項第2代傳統libbpf庫方案第3代libbpf-boots
21、trap方案第2代hexdump的libbpf庫改進方案獲取bpf.c的頭文件bpftool btf dump file/sys/kernel/btf/vmlinuxformat c vmlinux.hbpftool btf dump file/sys/kernel/btf/vmlinuxformat c vmlinux.hbpftool btf dump file/sys/kernel/btf/vmlinux format c vmlinux.h編譯bpf.o目標文件clang-O2-target bpf-c trace_execve.bpf.c-g-o trace_execve.bpf.o
22、clang-O2-target bpf-c trace_execve.bpf.c-g-o trace_execve.bpf.oclang-O2-target bpf-c trace_execve.bpf.c-g-o trace_execve.bpf.o生成skel.h頭文件無bpftool gen skeleton trace_execve.bpf.o trace_execve.skel.hhexdump使用skel.h頭文件無將程序名trace_execve添加到頭文件名稱中trace_execve.skel.h統一成一個固定的名稱skeleton.skel.h用戶態加載函數使用libbpf
23、庫標準加載函數bpf_object_open_file();bpf_object_load();bpf_program_attach();將程序名添加到加載函數名稱中trace_execve_bpf_open();trace_execve_bpf_load();trace_execve_bpf_attach();使用libbpf庫標準加載函數bpf_object_open_mem();bpf_object_load();bpf_program_attach();trace_execve_bpf_open()函數的實現,也是間接通過libbpf庫的bpf_object_open_skeleton
24、()函數,最終也調用了bpf_object_open_mem()函數。相比較第3代的 libbpf-bootstrap框架方案和第2代的傳統libbpf庫方案,使用hexdump命令的原生libbpf庫第 2 代改進方案方案在實現方法上,有一些獨特的優勢。使用attach_tracepoint替代attachtrace_execve.c中相關代碼trace_execve.bpf.c中相關代碼attach方案Abpf_program_attach(bpf_prog)SEC(tracepoint/syscalls/sys_enter_execve)attach方案Bbpf_program_atta
25、ch_tracepoint(bpf_prog,syscalls,sys_enter_execve)SEC(tracepoint)方案A在內核態bpf.c文件的SEC的節名稱中設置靜態探針點信息。bpf_program_attach不用指定靜態探針點的信息,會自動解析bpf.c目標文件中SEC的節名稱信息來獲取和確定靜態探針點的信息的。方案B在用戶態bpf_program_attach_tracepoint函數的參數中指定靜態探針點的具體信息。在trace_execve.c和trace_execve.bpf.c的代碼中,只要有一處設置靜態探針點即可。若都設置,并且設置的靜態探針點信息沖突的情況下
26、,會以用戶態的bpf_program_attach_tracepoint函數設置的信息為準。特別推薦使用方案B中的bpf_program_attach_tracepoint替代方案A中的bpf_program_attach方法,這樣方便在用戶態代碼中靈活的開關ebpf的采集。除了專門用于靜態探針點的bpf_program_attach_tracepoint()函數,還有適用于其他類型的專用的attach函數,例如bpf_program_attach_kprobe()、bpf_program_attach_kprobe()、bpf_program_attach_uprobe()和bpf_prog
27、ram_attach_usdt()等。使用by_name替代by_title早期libbpf庫中提供2個函數用于獲取bpf progam類型數據結構,分別是bpf_object_find_program_by_name()函數和bpf_object_find_program_by_title()函數。如圖中,tracepoint/syscalls/sys_enter_execve這個字符串就稱為title,trace_execve_enter這個函數名就稱為name。上文推薦bpf內核態代碼中都使用SEC(tracepoint)的語法格式,那么使用by_title函數將不再能做出區分。因此特別
28、推薦大家今后使用by_name的函數替代by_titile的函數。并且,在最新版的libbpf庫中,也徹底移除了bpf_object_find_program_by_title()函數支持。04低版本內核編程方案第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m中 國 西 安獲取BTF格式或DWARF格式的vmlinux 還可以安裝dwarves包,獲取pahole命令,進而提取到DWARF格式的vmlinux文件。CentOS環境ubuntu環境工具包yum install dwarvesapt-get updateapt install dwarves
29、DWARF包kernel-debuginfo-$(uname-r)kernel-debuginfo-common-x86_64-$(uname-r)linux-image-$(uname-r)-dbgsymlinux-image-unsigned-$(uname-r)-dbgsym推薦地址https:/ 低版本內核推薦通過龍蜥社區獲取BTF格式vmlinux。高版本內核在/sys/kernel/btf/vmlinux路徑會內建提供BTF格式的vmlinux文件,而低版本內核默認沒有提供。在此,我們推薦通過龍蜥社區下載BTF格式的vmlinux。https:/ BTF文件制作步驟 BTF文件制作
30、簡析從圖示看到,原始的vmlinx文件是一個elf格式文件,只有562M,擁有82個節。pahole-J會依賴其中的dwarf格式信息生成第83個.BTF節,此時vmlinux.add文件有565M。llvm-objcopy的dump-section選項,會將vmlinx.add文件中的.BTF節獨立保存到vmlinux.btf格式文件中,單獨保存的vmlinx.btf文件只有2.8M。有些發行版環境,還需要把vmlinux.btf轉為elf格式。如果pahole版本大于等于1.24,提取命令可簡化為:pahole-btf_encode_detached=vmlinux.btf-J vmlin
31、ux使用BTF格式的vmlinux 運行過程使用BTF文件 編譯過程使用BTF文件用戶態加載過程,libbpf庫會在btf.c文件的btf_load_vmlinux_btf()函數中,嘗試在如下文件路徑查找BTF文件,并加載。ebpf編譯過程,也需要使用bpftool命令,依賴BTF文件,生成vmlinux.h頭文件。推薦選擇此路徑05低版本編譯環境編程方案第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m中 國 西 安改進libbpf編程方案在低版本環境的應用比較項第2代hexdump的libbpf庫改進方案低版本內核環境低版本clang編譯工具環境獲取
32、bpf.c的頭文件bpftool btf dump file/sys/kernel/btf/vmlinuxformat c vmlinux.hbpftool btf dump file/boot/vmlinux-$(uname-r)format c vmlinux.h安裝kernel-devel rpm(或linux-headers deb)包編譯bpf.o目標文件clang-O2-target bpf-c trace_execve.bpf.c-g-o trace_execve.bpf.oclang-O2-target bpf-c trace_execve.bpf.c-o-g trace_ex
33、ecve.bpf.oclang-O2-emit-llvm-Xclang-c probe_execve.bpf.c-o-|opt-O2-mtriple=bpf-pc-linux|llvm-dis|llc-march=bpf-filetype=obj-o probe_execve.bpf.o生成skel.h頭文件hexdumphexdumphexdump使用skel.h頭文件統一成一個固定的名稱skeleton.skel.h統一成一個固定的名稱skeleton.skel.h統一成一個固定的名稱skeleton.skel.h用戶態加載函數使用libbpf庫標準加載函數bpf_object_open_
34、mem();bpf_object_load();bpf_program_attach();使用libbpf庫標準加載函數bpf_object_open_mem();bpf_object_load();bpf_program_attach();使用libbpf庫標準加載函數bpf_object_open_mem();bpf_object_load();bpf_program_attach();第2代libbpf庫改進方案在低版本環境也有很強的適用性:1.在低版本內核環境,需要用自制的BTF文件/boot/vmlinux-$(uname-r),替代/sys/kernel/btf/vmlinux。2
35、.clang版本低于9.0的屬于低版本編譯工具環境。此時可以盡量使用早期ebpf的開發方案,包括使用內核開發包kernel-devel頭文件替代 vmlinux.h,使用map替代.map,使用llc-march=bpf替代clang-target bpf編譯命令等。這些早期編譯方案的組合將具有極強的低版本編譯環境的兼容性。Talk is cheap.Show me the code third time.從運行結果看,不依賴vmlinux.h,僅依賴kernel-devel(linux-headers)包的頭文件編譯的bpf目標文件trace_execve.bpf.o,嵌入trace_exe
36、cve文件后,依然可以成功執行。低版本編譯環境示例:低版本編譯環境內核態bpf程序編譯參數解析編譯參數參數解析-I/lib/modules/$(shell uname-r)/build/arch/x86/include-I/lib/modules/$(shell uname-r)/build/arch/x86/include/generated-I/lib/modules/$(shell uname-r)/build/include-I/lib/modules/$(shell uname-r)/build/arch/x86/include/uapi-I/lib/modules/$(shell
37、uname-r)/build/arch/x86/include/generated/uapi-I/lib/modules/$(shell uname-r)/build/include/uapi-I/lib/modules/$(shell uname-r)/build/include/generated/uapi通過這一組的7個選項,可在這7個內核頭文件的搜索路徑下查找如下內核頭文件。#include#include#include#include-include/lib/modules/$(shell uname-r)/build/include/linux/kconfig.h等價于添加頭文件
38、#include 無需-g參數無需生成execve.bpf.o目標文件中的.BTF和.BTF.ext節。llc-march=bpf指示clang默認編譯是x86格式,通過llc命令再轉換為bpf格式。BTF風格和傳統風格map定義方式 傳統風格map定義方式 BTF風格map定義方式 0.8.1版本libbpf庫對map節的提取邏輯 1.3.0版本libbpf庫對map節的提取邏輯1.0版本以上libbpf庫已經禁用傳統風格的map定義方式。如果依然需要使用傳統風格map定義方式,需要使用0.8.1以下版本libbpf庫。Thanks第二屆 eBPF開發者大會w w w.e b p f t r a v e l.c o m釘釘加入龍蜥社區群釘釘加入龍蜥社區群群號群號3330400733304007微信加微信加sreworkssreworks小助手小助手邀請入群邀請入群