安裝開(kāi)發(fā)環(huán)境
必要的開(kāi)發(fā)工具:
# For Ubuntu20.10+
sudo apt-get install -y make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev linux-tools-$(uname -r) linux-headers-$(uname -r)
# For RHEL8.2+
sudo yum install libbpf-devel make clang llvm elfutils-libelf-devel bpftool bcc-tools bcc-devel
libbpf-dev 這個(gè)庫(kù)很可能需要從源碼安裝富玷,具體的步驟你可以參考 libbpf 的 GitHub 倉(cāng)庫(kù)悍引。
安裝可能出現(xiàn)問(wèn)題 ,參考:https://blog.csdn.net/dwh0403/article/details/120243023
開(kāi)發(fā)過(guò)程
ebpf的一些工具 https://www.brendangregg.com/ebpf.html
一般來(lái)說(shuō)掏颊,這個(gè)過(guò)程分為以下 5 步:
- 第一步,使用 C 語(yǔ)言開(kāi)發(fā)一個(gè) eBPF 程序;
- 第二步看锉,借助 LLVM 把 eBPF 程序編譯成 BPF 字節(jié)碼姿锭;
- 第三步,通過(guò) bpf 系統(tǒng)調(diào)用伯铣,把 BPF 字節(jié)碼提交給內(nèi)核呻此;
- 第四步,內(nèi)核驗(yàn)證并運(yùn)行 BPF 字節(jié)碼腔寡,并把相應(yīng)的狀態(tài)保存到 BPF 映射中焚鲜;
- 第五步,用戶程序通過(guò) BPF 映射查詢 BPF 字節(jié)碼的運(yùn)行狀態(tài)
使用 BCC 開(kāi)發(fā) eBPF 程序放前,可以把前面講到的五步簡(jiǎn)化為下面的三步忿磅。
第一步:使用 C 開(kāi)發(fā)一個(gè) eBPF 程序
新建一個(gè) hello.c 文件,并輸入下面的內(nèi)容:
int hello_world(void *ctx)
{
bpf_trace_printk("Hello, World!");
return 0;
}
就像所有編程語(yǔ)言的“ Hello World ”示例一樣凭语,這段代碼的含義就是打印一句 “Hello, World!” 字符串葱她。其中, bpf_trace_printk() 是一個(gè)最常用的 BPF 輔助函數(shù)似扔,它的作用是輸出一段字符串吨些。不過(guò),由于 eBPF 運(yùn)行在內(nèi)核中炒辉,它的輸出并不是通常的標(biāo)準(zhǔn)輸出(stdout)豪墅,而是內(nèi)核調(diào)試文件 /sys/kernel/debug/tracing/trace_pipe ,你可以直接使用 cat 命令來(lái)查看這個(gè)文件的內(nèi)容黔寇。
第二步:使用 Python 和 BCC 庫(kù)開(kāi)發(fā)一個(gè)用戶態(tài)程序
接下來(lái)偶器,創(chuàng)建一個(gè) hello.py 文件,并輸入下面的內(nèi)容
#!/usr/bin/env python3
# 1) import bcc library
from bcc import BPF
# 2) load BPF program
b = BPF(src_file="hello.c")
# 3) attach kprobe
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 4) read and print /sys/kernel/debug/tracing/trace_pipe
b.trace_print()
讓我們來(lái)看看每一處的具體含義:
- 第 1) 處導(dǎo)入了 BCC 庫(kù)的 BPF 模塊缝裤,以便接下來(lái)調(diào)用状囱;
- 第 2) 處調(diào)用 BPF() 加載第一步開(kāi)發(fā)的 BPF 源代碼;
- 第 3) 處將 BPF 程序掛載到內(nèi)核探針(簡(jiǎn)稱 kprobe)倘是,其中 do_sys_openat2() 是系統(tǒng)調(diào)用 openat() 在內(nèi)核中的實(shí)現(xiàn)亭枷;
- 第 4) 處則是讀取內(nèi)核調(diào)試文件 /sys/kernel/debug/tracing/trace_pipe 的內(nèi)容,并打印到標(biāo)準(zhǔn)輸出中搀崭。在運(yùn)行的時(shí)候叨粘,BCC 會(huì)調(diào)用 LLVM,把 BPF 源代碼編譯為字節(jié)碼瘤睹,再加載到內(nèi)核中運(yùn)行升敲。
第三步:執(zhí)行 eBPF 程序
用戶態(tài)程序開(kāi)發(fā)完成之后,最后一步就是執(zhí)行它了轰传。需要注意的是驴党, eBPF 程序需要以 root 用戶來(lái)運(yùn)行,非 root 用戶需要加上 sudo 來(lái)執(zhí)行:
sudo python3 hello.py
稍等一會(huì)获茬,你就可以看到如下的輸出:
b' cat-10656 [006] d... 2348.114455: bpf_trace_printk: Hello, World!'
輸出的格式可由 /sys/kernel/debug/tracing/trace_options 來(lái)修改港庄。比如前面這個(gè)默認(rèn)的輸出中倔既,每個(gè)字段的含義如下所示:
- cat-10656 表示進(jìn)程的名字和 PID;
- [006] 表示 CPU 編號(hào)鹏氧;
- d… 表示一系列的選項(xiàng)渤涌;2348.114455 表示時(shí)間戳;
- bpf_trace_printk 表示函數(shù)名把还;
- 最后的 “Hello, World!” 就是調(diào)用 bpf_trace_printk() 傳入的字符串实蓬。
到了這里,恭喜你已經(jīng)成功開(kāi)發(fā)并運(yùn)行了第一個(gè) eBPF 程序吊履!