1.介紹
eBPF(extened Berkeley Packet Filter)是一種內(nèi)核技術(shù),它允許開發(fā)人員在不修改內(nèi)核代碼的情況下運行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter(BPF),后者是由貝爾實驗室開發(fā)的一種網(wǎng)絡(luò)過濾器,可以捕獲和過濾網(wǎng)絡(luò)數(shù)據(jù)包。
出于對更好的 Linux 跟蹤工具的需求,eBPF 從 dtrace中汲取靈感,dtrace 是一種主要用于 Solaris 和 BSD 操作系統(tǒng)的動態(tài)跟蹤工具。與 dtrace 不同,Linux 無法全面了解正在運行的系統(tǒng),因為它僅限于系統(tǒng)調(diào)用、庫調(diào)用和函數(shù)的特定框架。
在 Berkeley Packet Filter(BPF)(一種使用內(nèi)核 VM 編寫打包過濾代碼的工具)的基礎(chǔ)上,一小群工程師開始擴展 BPF 后端以提供與 dtrace 類似的功能集。eBPF 誕生了。2014 年隨 Linux 3.18 首次限量發(fā)布,充分利用 eBPF 至少需要 Linux 4.4 以上版本。
eBPF 比起傳統(tǒng)的 BPF 來說,傳統(tǒng)的 BPF 只能用于網(wǎng)絡(luò)過濾,而 eBPF 則可以用于更多的應(yīng)用場景,包括網(wǎng)絡(luò)監(jiān)控、安全過濾和性能分析等。另外,eBPF 允許常規(guī)用戶空間應(yīng)用程序?qū)⒁?Linux 內(nèi)核中執(zhí)行的邏輯打包為字節(jié)碼,當某些事件(稱為掛鉤)發(fā)生時,內(nèi)核會調(diào)用 eBPF 程序。此類掛鉤的示例包括系統(tǒng)調(diào)用、網(wǎng)絡(luò)事件等。用于編寫和調(diào)試 eBPF 程序的最流行的工具鏈稱為 BPF 編譯器集合(BCC),它基于 LLVM 和 CLang。
eBPF 有一些類似的工具。例如,SystemTap 是一種開源工具,可以幫助用戶收集 Linux 內(nèi)核的運行時數(shù)據(jù)。它通過動態(tài)加載內(nèi)核模塊來實現(xiàn)這一功能,類似于 eBPF。另外,DTrace 是一種動態(tài)跟蹤和分析工具,可以用于收集系統(tǒng)的運行時數(shù)據(jù),類似于 eBPF 和 SystemTap。[Ⅰ]
以下是一個簡單的比較表格,可以幫助您更好地了解 eBPF、SystemTap 和 DTrace 這三種工具的不同之處:[Ⅰ]
從上表可以看出,eBPF、SystemTap 和 DTrace 都是非常強大的工具,可以用于收集和分析系統(tǒng)的運行情況。[Ⅰ]
用途
eBPF 是一種非常靈活和強大的內(nèi)核技術(shù),可以用于多種應(yīng)用場景。下面是 eBPF 的一些常見用途:[Ⅰ]
網(wǎng)絡(luò)監(jiān)控:eBPF 可以用于捕獲網(wǎng)絡(luò)數(shù)據(jù)包,并執(zhí)行特定的邏輯來分析網(wǎng)絡(luò)流量。例如,可以使用 eBPF 程序來監(jiān)控網(wǎng)絡(luò)流量,并在發(fā)現(xiàn)異常流量時進行警報。[Ⅰ]
安全過濾:eBPF 可以用于對網(wǎng)絡(luò)數(shù)據(jù)包進行安全過濾。例如,可以使用 eBPF 程序來阻止惡意流量的傳播,或者在發(fā)現(xiàn)惡意流量時對其進行攔截。[Ⅰ]
性能分析:eBPF 可以用于對內(nèi)核的性能進行分析。例如,可以使用 eBPF 程序來收集內(nèi)核的性能指標,并通過特定的接口將其可視化。這樣,可以更好地了解內(nèi)核的性能瓶頸,并進行優(yōu)化。[Ⅰ]
虛擬化:eBPF 可以用于虛擬化技術(shù)。例如,可以使用 eBPF 程序來收集虛擬機的性能指標,并進行負載均衡。這樣,可以更好地利用虛擬化環(huán)境的資源,提高系統(tǒng)的性能和穩(wěn)定性。[Ⅰ]
總之,eBPF 的常見用途非常廣泛,可以用于網(wǎng)絡(luò)監(jiān)控、安全過濾、性能分析和虛擬化等多種應(yīng)用場景。[Ⅰ]
2.工作原理
eBPF 的工作原理主要分為三個步驟:加載、編譯和執(zhí)行。
eBPF 需要在內(nèi)核中運行。這通常是由用戶態(tài)的應(yīng)用程序完成的,它會通過系統(tǒng)調(diào)用來加載 eBPF 程序。在加載過程中,內(nèi)核會將 eBPF 程序的代碼復(fù)制到內(nèi)核空間。
eBPF 程序需要經(jīng)過編譯和執(zhí)行。這通常是由Clang/LLVM的編譯器完成,然后形成字節(jié)碼后,將用戶態(tài)的字節(jié)碼裝載進內(nèi)核,并通過一個JIT編譯步驟將程序的通用字節(jié)碼轉(zhuǎn)換為機器特定指令集,以優(yōu)化程序的執(zhí)行速度。
在內(nèi)核中運行時,eBPF 程序通常會掛載到一個內(nèi)核鉤子(hook)上,以便在特定的事件發(fā)生時被執(zhí)行。例如,可以將 eBPF 程序掛載到網(wǎng)絡(luò)協(xié)議棧的某個位置,以便在收到網(wǎng)絡(luò)數(shù)據(jù)包時被執(zhí)行。
最后,eBPF 程序還需要經(jīng)過內(nèi)核安全機制的檢查。這是為了確保 eBPF 程序不會破壞內(nèi)核的穩(wěn)定性和安全性。在檢查過程中,內(nèi)核會對 eBPF 程序的代碼進行分析,以確保它不會進行惡意操作,如系統(tǒng)調(diào)用、內(nèi)存訪問等。如果 eBPF 程序通過了內(nèi)核安全機制的檢查,它就可以在內(nèi)核中正常運行了。在運行過程中,eBPF 程序可以訪問內(nèi)核的數(shù)據(jù)結(jié)構(gòu),并通過內(nèi)核接口與其他組件進行交互。例如,eBPF 程序可以捕獲網(wǎng)絡(luò)數(shù)據(jù)包,并通過內(nèi)核接口將它們轉(zhuǎn)發(fā)給用戶態(tài)的應(yīng)用程序。總之,eBPF 的工作原理是通過動態(tài)加載、執(zhí)行和檢查無損編譯過的代碼來實現(xiàn)的。[Ⅰ]
下圖是其架構(gòu)圖。
圖片來自:https://www.infoq.com/articles/gentle-linux-ebpf-introduction/
3. 示例
eBPF 可以用于對內(nèi)核的性能進行分析。下面是一個基于 eBPF 的性能分析的 step-by-step 示例:
第一步:準備工作:首先,需要確保內(nèi)核已經(jīng)支持 eBPF 功能。這通常需要在內(nèi)核配置文件中啟用 eBPF 相關(guān)的選項,并重新編譯內(nèi)核。檢查是否支持 eBPF,你可以用這兩個命令查看 ls /sys/fs/bpf 和 lsmod | grep bpf。
第二步:寫 eBPF 程序:接下來,需要編寫 eBPF 程序,用于收集內(nèi)核的性能指標。eBPF 程序的語言可以選擇 C 或者 Python,它需要通過特定的接口訪問內(nèi)核的數(shù)據(jù)結(jié)構(gòu),并將收集到的數(shù)據(jù)保存到指定的位置。
下面是一個 Python 示例:
#!/usr/bin/python3 frombccimportBPF fromtimeimportsleep #定義eBPF程序 bpf_text=""" #includeBPF_HASH(stats,u32); intcount(structpt_regs*ctx){ u32key=0; u64*val,zero=0; val=stats.lookup_or_init(&key,&zero); (*val)++; return0; } """ #編譯eBPF程序 b=BPF(text=bpf_text,cflags=["-Wno-macro-redefined"]) #加載eBPF程序 b.attach_kprobe(event="tcp_sendmsg",fn_name="count") name={ 0:"tcp_sendmsg" } #輸出統(tǒng)計結(jié)果 whileTrue: try: #print("Totalpackets:%d"%b["stats"][0].value) fork,vinb["stats"].items(): print("{}:{}".format(name[k.value],v.value)) sleep(1) exceptKeyboardInterrupt: exit()
這個 eBPF 程序的功能是統(tǒng)計網(wǎng)絡(luò)中傳輸?shù)臄?shù)據(jù)包數(shù)量。它通過定義一個 BPF_HASH 數(shù)據(jù)結(jié)構(gòu)來保存統(tǒng)計結(jié)果,并通過捕獲 tcp_sendmsg 事件來實現(xiàn)實時統(tǒng)計。最后,它通過每秒輸出一次統(tǒng)計結(jié)果來展示數(shù)據(jù)。這個 eBPF 程序只是一個簡單的示例,實際應(yīng)用中可能需要進行更復(fù)雜的統(tǒng)計和分析。
第三步:運行 eBPF 程序:接下來,需要使用eBPF編譯器將 eBPF 程序編譯成內(nèi)核可執(zhí)行的格式(這個在上面的Python程序里你可以看到——Python引入了一個bcc的包,然后用這個包,把那段 C語言的程序編譯成字節(jié)碼加載在內(nèi)核中并把某個函數(shù)attach到某個事件上)。這個過程可以使用 BPF Compiler Collection(BCC)工具來完成。BCC 工具可以通過命令行的方式將 eBPF 程序編譯成內(nèi)核可執(zhí)行的格式,并將其加載到內(nèi)核中。
下面是運行上面的 Python3 程序的步驟:
sudoaptinstallpython3-bpfcc
注:在Python3下請不要使用 pip3 install bcc (參看:https://github.com/iovisor/bcc/issues/2278#issuecomment-825356087)
如果你是 Ubuntu 20.10 以上的版本,最好通過源碼安裝(否則程序會有編譯問題),參看https://github.com/iovisor/bcc/issues/3993#issuecomment-1228217609:
aptpurgebpfcc-toolslibbpfccpython3-bpfcc wgethttps://github.com/iovisor/bcc/releases/download/v0.25.0/bcc-src-with-submodule.tar.gz tarxfbcc-src-with-submodule.tar.gz cdbcc/ aptinstall-ypython-is-python3 aptinstall-ybisonbuild-essentialcmakeflexgitlibedit-devlibllvm11llvm-11-devlibclang-11-devzlib1g-devlibelf-devlibfl-devpython3-distutils aptinstall-ycheckinstall mkdirbuild cdbuild/ cmake-DCMAKE_INSTALL_PREFIX=/usr-DPYTHON_CMD=python3.. make checkinstall
接下來,需要將上面的 Python 程序保存到本地,例如保存到文件 netstat.py。運行程序:最后,可以通過執(zhí)行以下命令來運行 Python 程序:
$chmod+x./netstat.py $sudo./netstat.py tcp_sendmsg:29 tcp_sendmsg:216 tcp_sendmsg:277 tcp_sendmsg:379 tcp_sendmsg:419 tcp_sendmsg:468 tcp_sendmsg:574 tcp_sendmsg:645 tcp_sendmsg:29
程序開始運行后,會在控制臺輸出網(wǎng)絡(luò)數(shù)據(jù)包的統(tǒng)計信息。可以通過按 Ctrl+C 組合鍵來結(jié)束程序的運行。
下面我們再看一個比較復(fù)雜的示例,這個示例會計算 TCP 的發(fā)包時間(示例參考于 Github 上這個 issue[1] 里的程序):
#!/usr/bin/python3 frombccimportBPF importtime #定義eBPF程序 bpf_text=""" #include#include #include #include structpacket_t{ u64ts,size; u32pid; u32saddr,daddr; u16sport,dport; }; BPF_HASH(packets,u64,structpacket_t); inton_send(structpt_regs*ctx,structsock*sk,structmsghdr*msg,size_tsize) { u64id=bpf_get_current_pid_tgid(); u32pid=id; //記錄數(shù)據(jù)包的時間戳和信息 structpacket_tpkt={};//結(jié)構(gòu)體一定要初始化,可以使用下面的方法 //__builtin_memset(&pkt,0,sizeof(pkt)); pkt.ts=bpf_ktime_get_ns(); pkt.size=size; pkt.pid=pid; pkt.saddr=sk->__sk_common.skc_rcv_saddr; pkt.daddr=sk->__sk_common.skc_daddr; structinet_sock*sockp=(structinet_sock*)sk; pkt.sport=sockp->inet_sport; pkt.dport=sk->__sk_common.skc_dport; packets.update(&id,&pkt); return0; } inton_recv(structpt_regs*ctx,structsock*sk) { u64id=bpf_get_current_pid_tgid(); u32pid=id; //獲取數(shù)據(jù)包的時間戳和編號 structpacket_t*pkt=packets.lookup(&id); if(!pkt){ return0; } //計算傳輸時間 u64delta=bpf_ktime_get_ns()-pkt->ts; //統(tǒng)計結(jié)果 bpf_trace_printk("tcp_time:%llu.%llums,size:%llu\n", delta/1000,delta%1000%100,pkt->size); //刪除統(tǒng)計結(jié)果 packets.delete(&id); return0; } """ #編譯eBPF程序 b=BPF(text=bpf_text,cflags=["-Wno-macro-redefined"]) #注冊eBPF程序 b.attach_kprobe(event="tcp_sendmsg",fn_name="on_send") b.attach_kprobe(event="tcp_v4_do_rcv",fn_name="on_recv") #輸出統(tǒng)計信息 print("TracingTCPlatency...HitCtrl-Ctoend.") whileTrue: try: (task,pid,cpu,flags,ts,msg)=b.trace_fields() print("%-18.9f%-16s%-6d%s"%(ts,task,pid,msg)) exceptKeyboardInterrupt: exit()
上面這個程序通過捕獲每個數(shù)據(jù)包的時間戳來統(tǒng)計傳輸時間。在捕獲 tcp_sendmsg 事件時,記錄數(shù)據(jù)包的發(fā)送時間;在捕獲 tcp_v4_do_rcv 事件時,記錄數(shù)據(jù)包的接收時間;最后,通過比較兩個時間戳來計算傳輸時間。
從上面的兩個程序我們可以看到,eBPF 的一個編程的基本方法,這樣的在 Python 里向內(nèi)核的某些事件掛載一段 “C語言” 的方式就是 eBPF 的編程方式。
實話實說,這樣的代碼很不好寫,而且有很多非常詭異的東西,一般人是很難駕馭的(上面的代碼我也很不是很容易都能寫通的,把 Google 都用了個底兒掉,讀了很多晦澀的文檔……)。好在這樣的代碼已經(jīng)有人寫了,我們不必再寫了,在 Github 上的 bcc 庫下的 tools 目錄[2]有很多……
BCC(BPF Compiler Collection)是一套開源的工具集,可以在 Linux 系統(tǒng)中使用 BPF(Berkeley Packet Filter)程序進行系統(tǒng)級性能分析和監(jiān)測。BCC 包含了許多實用工具,如:
bcc-tools:一個包含許多常用的 BCC 工具的軟件包。
bpftrace:一個高級語言,用于編寫和執(zhí)行 BPF 程序。
tcptop:一個實時監(jiān)控和分析 TCP 流量的工具。
execsnoop:一個用于監(jiān)控進程執(zhí)行情況的工具。
filetop:一個實時監(jiān)控和分析文件系統(tǒng)流量的工具。
trace:一個用于跟蹤和分析函數(shù)調(diào)用的工具。
funccount:一個用于統(tǒng)計函數(shù)調(diào)用次數(shù)的工具。
opensnoop:一個用于監(jiān)控文件打開操作的工具。
pidstat:一個用于監(jiān)控進程性能的工具。
profile:一個用于分析系統(tǒng) CPU 使用情況的工具。
下面這張圖你可能見過多次了,你可以看看他可以干多少事,內(nèi)核里發(fā)生什么事一覽無余。
4.延伸閱讀
一些經(jīng)典的文章和書籍關(guān)于 eBPF 包括:
Brendan Gregg 的《BPF Performance Tools: Linux System and Application - Observability[3]》一書是一個全面的指南,涵蓋了 eBPF 的基礎(chǔ)知識和實踐應(yīng)用。
eBPF 的官網(wǎng):https://ebpf.io/ 由 Cilium 建立
Cilium’s BPF and XDP Reference Guide:http://docs.cilium.io/en/latest/bpf/
BPF Documentation:https://www.kernel.org/doc/html/latest/bpf/index.html
BPF Design Q&A:https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html
還有 Github 上的 Awesome eBPF:https://github.com/zoidbergwill/awesome-ebpf
5. 彩蛋
最后來到彩蛋環(huán)節(jié)。因為最近 ChatGPT 很火,于是,我想通過 ChatGPT 來幫助我書寫這篇文章,一開始我讓 ChatGPT 幫我列提綱,并根據(jù)提綱生成文章內(nèi)容,并查找相關(guān)的資料,非常之順利,包括生成的代碼,我以為我們以很快地完成這篇文章。
但是,到了代碼生成的時候,我發(fā)現(xiàn),ChatGPT 生成的代碼的思路和方法都是對的,但是是比較老的,而且是跑不起來的,出現(xiàn)了好些低級錯誤,如:使用了未聲明的變量,沒有引用完整的C語言的頭文件,沒有正確地初始化變量,錯誤地獲取數(shù)據(jù),類型沒有匹配……等等,在程序調(diào)試上,挖了很多的坑,C 語言本來就不好搞,挖的很多運行時的坑很難察覺。
所以,耗費了我大量的時間來排除各種各樣的問題,其中有環(huán)境上的問題,還有代碼上的問題,這些問題即便是通過 Google 也不容易找到解決方案,我找到的解決方案都放在文章中了,尤其是第二個示例,讓我調(diào)試了3個多小時,讀了很多 bcc 上的 issue 和相關(guān)的晦澀的手冊和文檔,才讓程序跑通。
到了文章收關(guān)的階段,我讓 ChatGPT 給我?guī)讉€延伸閱讀,也是很好的,但是沒有給出鏈接,于是我只得人肉 Google 了一下,然后讓我吃驚的是,好多 ChatGPT 給出來的文章是根本不存在的,完全是它偽造的。我連讓它干了兩次都是這樣,這個讓我驚掉大牙。
這讓我開始懷疑它之前生成的內(nèi)容,于是,我不得我返回仔細 Review 我的文章,尤其是“介紹”、“用途”和“工作原理”這三個章節(jié),基本都是 ChatGPT 生成的,在 Review 完后,我發(fā)現(xiàn)了 ChatGPT 給我生造了一個叫 “無損編譯器”的術(shù)語,這個術(shù)語簡直了,于是我開始重寫我的文章。我把一些段落重寫了,有一些沒有,保留下來的我都標記上了 [Ⅰ],大家讀的時候要小心閱讀。
最后,我的結(jié)論是,ChatGPT 只是一個不成熟的玩具,只能回答一些沒有價值的日常聊天的問題,要說能取代 Google,我覺得不可能,因為 Google 會基于基本的事實,而 ChatGPT 會基于內(nèi)容生成的算法,在造假方面稱得上是高手,可以列為電信詐騙的范疇了,我以后不會再使用 ChatGPT 生成文章內(nèi)容或是作我的幫手了。StackOverflow 把其 ban 了真是不能太贊了!
審核編輯:湯梓紅
-
內(nèi)核
+關(guān)注
關(guān)注
3文章
1402瀏覽量
40897 -
Linux
+關(guān)注
關(guān)注
87文章
11411瀏覽量
212229 -
開源
+關(guān)注
關(guān)注
3文章
3524瀏覽量
43238 -
代碼
+關(guān)注
關(guān)注
30文章
4874瀏覽量
69938 -
觀測
+關(guān)注
關(guān)注
0文章
20瀏覽量
9668
原文標題:Linux 內(nèi)核觀測技術(shù) eBPF 中文入門指南
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
FreeRTOS實時內(nèi)核使用指南_中文
關(guān)于 eBPF 安全可觀測性,你需要知道的那些事兒
openEuler 倡議建立 eBPF 軟件發(fā)布標準
Linux內(nèi)核解讀入門
淺談Linux內(nèi)核解讀入門
linux內(nèi)核入門教材之linux內(nèi)核設(shè)計與實現(xiàn)第二版中文版免費下載

教你們?nèi)绾问褂?b class='flag-5'>eBPF追蹤LINUX內(nèi)核

介紹eBPF針對可觀測場景的應(yīng)用
eBPF安全可觀測性的前景展望
Linux技術(shù):eBPF內(nèi)核原理及應(yīng)用
Linux 內(nèi)核:eBPF優(yōu)勢和eBPF潛力總結(jié)
eBPF,何以稱得上是革命性的內(nèi)核技術(shù)?

基于ebpf的性能工具-bpftrace

評論