1.介紹
eBPF(extened Berkeley Packet Filter)是一種內核技術,它允許開發人員在不修改內核代碼的情況下運行特定的功能。eBPF 的概念源自于 Berkeley Packet Filter(BPF),后者是由貝爾實驗室開發的一種網絡過濾器,可以捕獲和過濾網絡數據包。
出于對更好的 Linux 跟蹤工具的需求,eBPF 從 dtrace中汲取靈感,dtrace 是一種主要用于 Solaris 和 BSD 操作系統的動態跟蹤工具。與 dtrace 不同,Linux 無法全面了解正在運行的系統,因為它僅限于系統調用、庫調用和函數的特定框架。
在 Berkeley Packet Filter(BPF)(一種使用內核 VM 編寫打包過濾代碼的工具)的基礎上,一小群工程師開始擴展 BPF 后端以提供與 dtrace 類似的功能集。eBPF 誕生了。2014 年隨 Linux 3.18 首次限量發布,充分利用 eBPF 至少需要 Linux 4.4 以上版本。
eBPF 比起傳統的 BPF 來說,傳統的 BPF 只能用于網絡過濾,而 eBPF 則可以用于更多的應用場景,包括網絡監控、安全過濾和性能分析等。另外,eBPF 允許常規用戶空間應用程序將要在 Linux 內核中執行的邏輯打包為字節碼,當某些事件(稱為掛鉤)發生時,內核會調用 eBPF 程序。此類掛鉤的示例包括系統調用、網絡事件等。用于編寫和調試 eBPF 程序的最流行的工具鏈稱為 BPF 編譯器集合(BCC),它基于 LLVM 和 CLang。
eBPF 有一些類似的工具。例如,SystemTap 是一種開源工具,可以幫助用戶收集 Linux 內核的運行時數據。它通過動態加載內核模塊來實現這一功能,類似于 eBPF。另外,DTrace 是一種動態跟蹤和分析工具,可以用于收集系統的運行時數據,類似于 eBPF 和 SystemTap。[Ⅰ]
以下是一個簡單的比較表格,可以幫助您更好地了解 eBPF、SystemTap 和 DTrace 這三種工具的不同之處:[Ⅰ]
從上表可以看出,eBPF、SystemTap 和 DTrace 都是非常強大的工具,可以用于收集和分析系統的運行情況。[Ⅰ]
用途
eBPF 是一種非常靈活和強大的內核技術,可以用于多種應用場景。下面是 eBPF 的一些常見用途:[Ⅰ]
網絡監控:eBPF 可以用于捕獲網絡數據包,并執行特定的邏輯來分析網絡流量。例如,可以使用 eBPF 程序來監控網絡流量,并在發現異常流量時進行警報。[Ⅰ]
安全過濾:eBPF 可以用于對網絡數據包進行安全過濾。例如,可以使用 eBPF 程序來阻止惡意流量的傳播,或者在發現惡意流量時對其進行攔截。[Ⅰ]
性能分析:eBPF 可以用于對內核的性能進行分析。例如,可以使用 eBPF 程序來收集內核的性能指標,并通過特定的接口將其可視化。這樣,可以更好地了解內核的性能瓶頸,并進行優化。[Ⅰ]
虛擬化:eBPF 可以用于虛擬化技術。例如,可以使用 eBPF 程序來收集虛擬機的性能指標,并進行負載均衡。這樣,可以更好地利用虛擬化環境的資源,提高系統的性能和穩定性。[Ⅰ]
總之,eBPF 的常見用途非常廣泛,可以用于網絡監控、安全過濾、性能分析和虛擬化等多種應用場景。[Ⅰ]
2.工作原理
eBPF 的工作原理主要分為三個步驟:加載、編譯和執行。
eBPF 需要在內核中運行。這通常是由用戶態的應用程序完成的,它會通過系統調用來加載 eBPF 程序。在加載過程中,內核會將 eBPF 程序的代碼復制到內核空間。
eBPF 程序需要經過編譯和執行。這通常是由Clang/LLVM的編譯器完成,然后形成字節碼后,將用戶態的字節碼裝載進內核,并通過一個JIT編譯步驟將程序的通用字節碼轉換為機器特定指令集,以優化程序的執行速度。
在內核中運行時,eBPF 程序通常會掛載到一個內核鉤子(hook)上,以便在特定的事件發生時被執行。例如,可以將 eBPF 程序掛載到網絡協議棧的某個位置,以便在收到網絡數據包時被執行。
最后,eBPF 程序還需要經過內核安全機制的檢查。這是為了確保 eBPF 程序不會破壞內核的穩定性和安全性。在檢查過程中,內核會對 eBPF 程序的代碼進行分析,以確保它不會進行惡意操作,如系統調用、內存訪問等。如果 eBPF 程序通過了內核安全機制的檢查,它就可以在內核中正常運行了。在運行過程中,eBPF 程序可以訪問內核的數據結構,并通過內核接口與其他組件進行交互。例如,eBPF 程序可以捕獲網絡數據包,并通過內核接口將它們轉發給用戶態的應用程序。總之,eBPF 的工作原理是通過動態加載、執行和檢查無損編譯過的代碼來實現的。[Ⅰ]
下圖是其架構圖。
圖片來自:https://www.infoq.com/articles/gentle-linux-ebpf-introduction/
3. 示例
eBPF 可以用于對內核的性能進行分析。下面是一個基于 eBPF 的性能分析的 step-by-step 示例:
第一步:準備工作:首先,需要確保內核已經支持 eBPF 功能。這通常需要在內核配置文件中啟用 eBPF 相關的選項,并重新編譯內核。檢查是否支持 eBPF,你可以用這兩個命令查看 ls /sys/fs/bpf 和 lsmod | grep bpf。
第二步:寫 eBPF 程序:接下來,需要編寫 eBPF 程序,用于收集內核的性能指標。eBPF 程序的語言可以選擇 C 或者 Python,它需要通過特定的接口訪問內核的數據結構,并將收集到的數據保存到指定的位置。
下面是一個 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" } #輸出統計結果 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 程序的功能是統計網絡中傳輸的數據包數量。它通過定義一個 BPF_HASH 數據結構來保存統計結果,并通過捕獲 tcp_sendmsg 事件來實現實時統計。最后,它通過每秒輸出一次統計結果來展示數據。這個 eBPF 程序只是一個簡單的示例,實際應用中可能需要進行更復雜的統計和分析。
第三步:運行 eBPF 程序:接下來,需要使用eBPF編譯器將 eBPF 程序編譯成內核可執行的格式(這個在上面的Python程序里你可以看到——Python引入了一個bcc的包,然后用這個包,把那段 C語言的程序編譯成字節碼加載在內核中并把某個函數attach到某個事件上)。這個過程可以使用 BPF Compiler Collection(BCC)工具來完成。BCC 工具可以通過命令行的方式將 eBPF 程序編譯成內核可執行的格式,并將其加載到內核中。
下面是運行上面的 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。運行程序:最后,可以通過執行以下命令來運行 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
程序開始運行后,會在控制臺輸出網絡數據包的統計信息。可以通過按 Ctrl+C 組合鍵來結束程序的運行。
下面我們再看一個比較復雜的示例,這個示例會計算 TCP 的發包時間(示例參考于 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; //記錄數據包的時間戳和信息 structpacket_tpkt={};//結構體一定要初始化,可以使用下面的方法 //__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; //獲取數據包的時間戳和編號 structpacket_t*pkt=packets.lookup(&id); if(!pkt){ return0; } //計算傳輸時間 u64delta=bpf_ktime_get_ns()-pkt->ts; //統計結果 bpf_trace_printk("tcp_time:%llu.%llums,size:%llu\n", delta/1000,delta%1000%100,pkt->size); //刪除統計結果 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") #輸出統計信息 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()
上面這個程序通過捕獲每個數據包的時間戳來統計傳輸時間。在捕獲 tcp_sendmsg 事件時,記錄數據包的發送時間;在捕獲 tcp_v4_do_rcv 事件時,記錄數據包的接收時間;最后,通過比較兩個時間戳來計算傳輸時間。
從上面的兩個程序我們可以看到,eBPF 的一個編程的基本方法,這樣的在 Python 里向內核的某些事件掛載一段 “C語言” 的方式就是 eBPF 的編程方式。
實話實說,這樣的代碼很不好寫,而且有很多非常詭異的東西,一般人是很難駕馭的(上面的代碼我也很不是很容易都能寫通的,把 Google 都用了個底兒掉,讀了很多晦澀的文檔……)。好在這樣的代碼已經有人寫了,我們不必再寫了,在 Github 上的 bcc 庫下的 tools 目錄[2]有很多……
BCC(BPF Compiler Collection)是一套開源的工具集,可以在 Linux 系統中使用 BPF(Berkeley Packet Filter)程序進行系統級性能分析和監測。BCC 包含了許多實用工具,如:
bcc-tools:一個包含許多常用的 BCC 工具的軟件包。
bpftrace:一個高級語言,用于編寫和執行 BPF 程序。
tcptop:一個實時監控和分析 TCP 流量的工具。
execsnoop:一個用于監控進程執行情況的工具。
filetop:一個實時監控和分析文件系統流量的工具。
trace:一個用于跟蹤和分析函數調用的工具。
funccount:一個用于統計函數調用次數的工具。
opensnoop:一個用于監控文件打開操作的工具。
pidstat:一個用于監控進程性能的工具。
profile:一個用于分析系統 CPU 使用情況的工具。
下面這張圖你可能見過多次了,你可以看看他可以干多少事,內核里發生什么事一覽無余。
4.延伸閱讀
一些經典的文章和書籍關于 eBPF 包括:
Brendan Gregg 的《BPF Performance Tools: Linux System and Application - Observability[3]》一書是一個全面的指南,涵蓋了 eBPF 的基礎知識和實踐應用。
eBPF 的官網: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. 彩蛋
最后來到彩蛋環節。因為最近 ChatGPT 很火,于是,我想通過 ChatGPT 來幫助我書寫這篇文章,一開始我讓 ChatGPT 幫我列提綱,并根據提綱生成文章內容,并查找相關的資料,非常之順利,包括生成的代碼,我以為我們以很快地完成這篇文章。
但是,到了代碼生成的時候,我發現,ChatGPT 生成的代碼的思路和方法都是對的,但是是比較老的,而且是跑不起來的,出現了好些低級錯誤,如:使用了未聲明的變量,沒有引用完整的C語言的頭文件,沒有正確地初始化變量,錯誤地獲取數據,類型沒有匹配……等等,在程序調試上,挖了很多的坑,C 語言本來就不好搞,挖的很多運行時的坑很難察覺。
所以,耗費了我大量的時間來排除各種各樣的問題,其中有環境上的問題,還有代碼上的問題,這些問題即便是通過 Google 也不容易找到解決方案,我找到的解決方案都放在文章中了,尤其是第二個示例,讓我調試了3個多小時,讀了很多 bcc 上的 issue 和相關的晦澀的手冊和文檔,才讓程序跑通。
到了文章收關的階段,我讓 ChatGPT 給我幾個延伸閱讀,也是很好的,但是沒有給出鏈接,于是我只得人肉 Google 了一下,然后讓我吃驚的是,好多 ChatGPT 給出來的文章是根本不存在的,完全是它偽造的。我連讓它干了兩次都是這樣,這個讓我驚掉大牙。
這讓我開始懷疑它之前生成的內容,于是,我不得我返回仔細 Review 我的文章,尤其是“介紹”、“用途”和“工作原理”這三個章節,基本都是 ChatGPT 生成的,在 Review 完后,我發現了 ChatGPT 給我生造了一個叫 “無損編譯器”的術語,這個術語簡直了,于是我開始重寫我的文章。我把一些段落重寫了,有一些沒有,保留下來的我都標記上了 [Ⅰ],大家讀的時候要小心閱讀。
最后,我的結論是,ChatGPT 只是一個不成熟的玩具,只能回答一些沒有價值的日常聊天的問題,要說能取代 Google,我覺得不可能,因為 Google 會基于基本的事實,而 ChatGPT 會基于內容生成的算法,在造假方面稱得上是高手,可以列為電信詐騙的范疇了,我以后不會再使用 ChatGPT 生成文章內容或是作我的幫手了。StackOverflow 把其 ban 了真是不能太贊了!
審核編輯:湯梓紅
-
內核
+關注
關注
3文章
1336瀏覽量
40081 -
Linux
+關注
關注
87文章
11123瀏覽量
207883 -
開源
+關注
關注
3文章
3123瀏覽量
42065 -
代碼
+關注
關注
30文章
4668瀏覽量
67753 -
觀測
+關注
關注
0文章
19瀏覽量
9592
原文標題:Linux 內核觀測技術 eBPF 中文入門指南
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論