BPF 之巔:洞悉 Linux 系統(tǒng)和應用性能

以下內容節(jié)選自《BPF之巔:洞悉Linux系統(tǒng)和應用性能》一書!

▊ BPF 和 eBPF 是什么
BPF 是 Berkeley Packet Filter(伯克利數(shù)據(jù)包過濾器)的縮寫,這項冷門技術誕生于 1992 年,其作用是提升網(wǎng)絡包過濾工具的性能。2013 年,Alexei Starovoitov 向 Linux 社區(qū)提交了重新實現(xiàn) BPF 的內核補丁,經(jīng)過他和 Daniel Borkmann 的共同完善, 相關工作在 2014 年正式并入 Linux 內核主線。此舉將 BPF變成了一個更通用的執(zhí)行引擎,其可以完成多種任務,包括用來創(chuàng)建先進的性能分析工具。
精確地解釋 BPF 的作用比較困難,因為它能做的事情實在太多了。簡單來說,BPF 提供了一種在各種內核事件和應用程序事件發(fā)生時運行一段小程序的機制。如果你熟悉 JavaScript,可能會看到一些相似之處 :JavaScript 允許網(wǎng)站在瀏覽器中發(fā)生某事件(比如鼠標單擊)時運行一段小程序,這樣就催生了各式各樣基于 Web 的應用程序。BPF 則允許內核在系統(tǒng)和應用程序事件(如磁盤 I/O 事件)發(fā)生時運行一段小程序,這樣就催生了新的系統(tǒng)編程技術。該技術將內核變得完全可編程,允許用戶(包括非專業(yè)內核 開發(fā)人員)定制和控制他們的系統(tǒng),以解決現(xiàn)實問題。
BPF 是一項靈活而高效的技術,由指令集、存儲對象和輔助函數(shù)等幾部分組成。由于它采用了虛擬指令集規(guī)范,因此也可將它視作一種虛擬機實現(xiàn)。這些指令由 Linux 內核的 BPF 運行時模塊執(zhí)行,具體來說,該運行時模塊提供兩種執(zhí)行機制 :一個解釋器和一個將 BPF 指令動態(tài)轉換為本地化指令的即時(JIT)編譯器。在實際執(zhí)行之前,BPF 指令必須先通過驗證器(verifer)的安全性檢查,以確保 BPF 程序自身不會崩潰或者損壞內核(當然這不會阻止最終用戶編寫出不合邏輯的程序—那些雖可執(zhí)行但沒意義的程序)。
目前 BPF 的三個主要應用領域分別是網(wǎng)絡、可觀測性和安全。本書主要關注可觀測性(跟蹤)。
擴展后的 BPF 通??s寫為 eBPF,但官方的縮寫仍然是 BPF,不帶“e”,所以在本 書中,筆者用 BPF 代表擴展后的 BPF。事實上,在內核中只有一個執(zhí)行引擎,即 BPF(擴展后的 BPF),它同時支持擴展后的 BPF 和“經(jīng)典”的 BPF 程序。
▊ 跟蹤、嗅探、采樣、剖析和可觀測性分別是什么
這些全都是用來對分析技術和工具進行分類的術語。
跟蹤(tracing)是基于事件的記錄—這也是 BPF工具所使用的監(jiān)測方式。你可能已經(jīng)使用過一些特定用途的跟蹤工具。例如,Linux 下的 strace(1),可以記錄和打印系統(tǒng)調用(system call)事件的信息。有許多工具并不跟蹤事件,而是使用固定的計數(shù)器統(tǒng)計監(jiān)測事件的頻次,然后打印出摘要信息 ;Linux top(1) 便是這樣的一個例子。跟蹤工具的一個顯著標志是,它具備記錄原始事件和事件元數(shù)據(jù)的能力。但是這類數(shù)據(jù)的數(shù)量不少,因此可能需要經(jīng)過后續(xù)處理生成摘要信息。BPF 技術,催生了可編程的跟蹤工具的出現(xiàn),這些工具可以在事件發(fā)生時,通過運行一段小程序來進行定制化的實時統(tǒng)計摘要生成或其他動作。
strace(1) 的名字中有“trace”(跟蹤)字樣,但并非所有跟蹤工具的名字中都帶 “trace”。例如,tcpdump(8) 是一個專門用于網(wǎng)絡數(shù)據(jù)包的跟蹤工具。(也許它應該被命 名為 tcptrace ?)Solaris 操作系統(tǒng)有它自己的 tcpdump 版本,稱為 snoop(1M)(嗅探器);之所以起這個名字,是因為它是用來嗅探網(wǎng)絡數(shù)據(jù)包的。筆者先前在 Solaris 系統(tǒng)上開發(fā) 和發(fā)布了許多跟蹤工具,在那里我(有一絲后悔)普遍使用了“嗅探器”來命名那些工具。這也是為什么現(xiàn)在會有下面這些工具:execsnoop(8)、opensnoop(8)、biosnoop(8) 等。嗅探、 事件記錄和跟蹤,通常指的是一回事。這些工具將在后面的章節(jié)中加以介紹。
除了工具的名稱,“tracing”一詞也經(jīng)常用于描述將 BPF 應用于可觀測性方面的用途。Linux 內核開發(fā)人員尤其喜歡這么表達。
采樣(sampling)工具通過獲取全部觀測量的子集來描繪目標的大致圖像 ;這也被 稱作生成性能剖析樣本或 pro?ling。有一個 BPF 工具就叫 pro?le(8),它基于計時器來對運行中的代碼定時采樣。例如,它可以每 10 毫秒采樣一次,換句話說,它可以每秒采樣 100 次(在每個 CPU 上)。采樣工具的一個優(yōu)點是,其性能開銷比跟蹤工具小,因為 只對大量事件中的一部分進行測量。采樣的缺點是,它只提供了一個大致的畫像,會遺漏事件。
可觀測性(observability)是指通過全面觀測來理解一個系統(tǒng),可以實現(xiàn)這一目標的工具就可以歸類為可觀測性工具。這其中包括跟蹤工具、采樣工具和基于固定計數(shù)器的工具。但不包括基準測量(benchmark)工具,基準測量工具在系統(tǒng)上模擬業(yè)務負載,會更改系統(tǒng)的狀態(tài)。本書中的 BPF 工具就屬于可觀測性工具,它們使用 BPF 技術進行 可編程型跟蹤分析。
▊ 初識 BCC:快速上手
讓我們直接切入主題,快速上手來看一些工具的輸出吧。下面這個工具會跟蹤每 個新創(chuàng)建的進程,并且為每次進程創(chuàng)建打印一行信息。這個叫 execsnoop(8) 的工具來自 BCC 項目,它通過跟蹤 execve(2) 系統(tǒng)調用來工作。execve(2) 是 exec(2) 系統(tǒng)調用的一 個變體(也因而得名)。第 4 章會介紹 BCC 工具的安裝,再往后的章節(jié)會更詳細地介紹相關工具。
# execsnoop | |||
PCOMM | PID | PPID | RET ARGS |
run | 12983 | 4469 | 0 ./run |
bash | 12983 | 4469 | 0 /bin/bash |
svstat | 12985 | 12984 | 0 /command/svstat /service/httpd |
perl | 12986 | 12984 | 0 /usr/bin/perl -e $l=<>;$l=~/(\d+) sec/;print $1||0 |
ps | 12988 | 12987 | 0 /bin/ps --ppid 1 -o pid,cmd,args |
grep | 12989 | 12987 | 0 /bin/grep org.apache.catalina |
sed | 12990 | 12987 | 0 /bin/sed s/^ *//; |
cut | 12991 | 12987 | 0 /usr/bin/cut -d -f 1 |
xargs | 12992 | 12987 | 0 /usr/bin/xargs |
echo | 12993 | 12992 | 0 /bin/echo |
mkdir | 12994 | 12983 | 0 /bin/mkdir -v -p /data/tomcat |
mkdir | 12995 | 12983 | 0 /bin/mkdir -v -p /apps/tomcat/webapps |
^C | |||
# |
上面的輸出顯示了在執(zhí)行跟蹤的過程中,系統(tǒng)創(chuàng)建了哪些進程 :其中有些進程運行 時間太短,因而使用其他工具可能無法捕獲到相關信息。在輸出中都能看到大量標準的 UNIX 工具:ps(1)、grep(1)、sed(1)、cut(1) 等。但是在這里你無法看到這個命令的打印速度。execsnoop(8) 帶上命令行參數(shù) -t 后,會增加一列時間戳輸出 :
# execsnoop -t
TIME(s) | PCOMM | PID | PPID | RET ARGS |
0.437 | run | 15524 | 4469 | 0 ./run |
0.438 | bash | 15524 | 4469 | 0 /bin/bash |
0.440 | svstat | 15526 | 15525 | 0 /command/svstat /service/httpd |
0.440 | perl | 15527 | 15525 | 0 /usr/bin/perl -e $l=<>;$l=~/(\d+) sec/;prin... |
0.442 | ps | 15529 | 15528 | 0 /bin/ps --ppid 1 -o pid,cmd,args |
[...] | ||||
0.487 | catalina.sh | 15524 | 4469 | 0 /apps/tomcat/bin/catalina.sh start |
0.488 | dirname | 15549 | 15524 | 0 /usr/bin/dirname /apps/tomcat/bin/catalina.sh |
1.459 | run | 15550 | 4469 | 0 ./run |
1.459 | bash | 15550 | 4469 | 0 /bin/bash |
1.462 | svstat | 15552 | 15551 | 0 /command/svstat /service/nflx-httpd |
1.462 | perl | 15553 | 15551 | 0 /usr/bin/perl -e $l=<>;$l=~/(\d+) sec/;prin... |
[...] |
上述輸出進行了截斷(用 [...] 表示),但時間戳那列信息還是顯示了一個新的線索 :新進程的批量創(chuàng)建之間有 1 秒的間隔,而且這個模式不斷重復。通過瀏覽輸出可以發(fā)現(xiàn),每秒會批量創(chuàng)建 30 個新的進程,然后停頓 1 秒,繼續(xù)批量創(chuàng)建 30 個新的進程。
上述輸出結果取自筆者在 Net?ix 公司調查真實性能問題時使用 execsnoop(8) 的過程。這臺服務器的作用是進行微基準測試,但問題是每次基準測試的結果差異很大,影響了 可信度。筆者在系統(tǒng)空閑時運行了 execsnoop(8),事實證明并非如我所想!每秒都有很 多進程被創(chuàng)建出來,這些進程對基準測試造成了干擾。最終,我們發(fā)現(xiàn),因為有一個服 務的配置不正確,導致它每秒都會被拉起、失敗,然后再被拉起,如此反復。當把這個 服務徹底禁止之后,就沒有新的進程被創(chuàng)建出來了(同樣可以使用 execsnoop(8) 進行驗 證),基準測試的數(shù)值也穩(wěn)定了下來。
execsnoop(8) 的輸出可以用來輔助支撐一個性能分析方法論:業(yè)務負載畫像(workloadcharacterization),本書中涉及的其他 BPF 工具的功能也都支持該方法論。業(yè)務負載畫像 方法論其實很簡單,就是給當前業(yè)務負載定性。理解了業(yè)務負載,很多時候就足夠解決 問題了,這避免了深入分析延遲問題,也不需要進行下鉆分析(drill-down analysis)。在本案例中,業(yè)務負載就是這些不斷有進程的創(chuàng)建。
請你嘗試在自己的系統(tǒng)上運行 execsnoop(8),并且讓它運行 1 小時,看看是否有所發(fā)現(xiàn)?
execsnoop(8) 會在每個進程創(chuàng)建時打印信息,而其他一些 BPF 工具則可以高效地計 算摘要統(tǒng)計信息。另一個可以快速上手的工具是 biolatency(8),它可以繪制塊設備 I/O(disk I/O)的延遲直方圖。
下面是在一臺生產(chǎn)環(huán)境中的數(shù)據(jù)庫服務器上運行 biolatency(8) 的輸出,該數(shù)據(jù)庫對 延遲非常敏感,因為該服務的服務質量目標(service level agreement)只有幾毫秒。
# biolatency -m
Tracing block device I/O... Hit Ctrl-C to end.
^C
msecs | : count | distribution | ||
0 -> | 1 | : 16335 | |****************************************| | |
2 -> | 3 | : 2272 | |***** | | |
4 -> | 7 | : 3603 | |******** | | |
8 -> | 15 | : 4328 | |********** | | |
16 -> | 31 | : 3379 | |******** | | |
32 -> | 63 | : 5815 | |************** | | |
64 -> | 127 | : 0 | | | | |
128 -> | 255 | : 0 | | | | |
256 -> | 511 | : 0 | | | | |
512 -> | 1023 | : 11 | | | | |
當 biolatency(8) 工具運行時會監(jiān)測塊 I/O 事件,它們的延遲信息通過 BPF 程序進行 計算和統(tǒng)計。當工具停止執(zhí)行后(用戶按下 Ctrl+C 組合鍵),摘要信息就被打印出來了。筆者使用了命令行參數(shù) -m 來使得統(tǒng)計值以毫秒為單位輸出。
上面的輸出結果中有一些有趣的細節(jié) :它呈現(xiàn)了雙峰分布特征,并且顯示了延遲離 群點的存在。第一峰(圖中用 ASCII 字符展示)是 0 ~ 1 毫秒這個區(qū)間,在跟蹤時有共 計 16 335 個 I/O 事件。這個速度相當快,可能是因為命中了存儲設備上的緩存或者使用的是閃存設備。第二峰是 32 ~ 63 毫秒這個區(qū)間,這相對此類存儲設備的預期性能慢了 不少,意味著可能有排隊發(fā)生。可以用更多的 BPF 工具深入調查進行確認。最后,對于 512 ~ 1023 毫秒?yún)^(qū)間,有 11 個 I/O 事件。這些極大的延遲稱為延遲離群點。現(xiàn)在我們知道了有這樣的離群點存在,后面就可以使用其他 BPF 工具來進一步定位。對于數(shù)據(jù)庫 團隊,這是需要高優(yōu)先級研究和解決的問題,因為一旦數(shù)據(jù)庫阻塞在了這些 I/O 請求上, 數(shù)據(jù)庫的延遲服務質量承諾就無法達到了。
震撼全球的 Gregg 大師新作
作為性能分析領域享譽全球的專家,Brendan Gregg 也是BPF項目的核心開發(fā)者,同時也是經(jīng)典暢銷著作《性能之巔:洞悉系統(tǒng)、企業(yè)與云計算》的作者。
沒有人比他更適合來寫這樣一本利用 BPF 工具進行性能分析的書了!
書中有150多個可以立即使用的分析調試工具及其應用場景,并且提供開發(fā)自定義工具的分步指南。

其中有數(shù)十個強大的工具是專門為本書而開發(fā),并可下載使用!
了解更多BPF技術內幕,推薦閱讀《BPF之巔:洞悉Linux系統(tǒng)和應用性能》一書。


【美】Brendan Gregg 著
孫宇聰 呂宏利 劉曉舟 譯
Gregg大師新作,《性能之巔》再續(xù)新篇
性能優(yōu)化的萬用金典,150+分析調試工具深度剖析
本書作為全面介紹 BPF 技術的圖書,從 BPF 技術的起源到未來發(fā)展方向都有涵蓋,不僅全面介紹了 BPF 的編程模型,還完整介紹了兩個主要的 BPF 前端編程框架 — BCC 和 bpftrace,更給出了一系列實現(xiàn)范例,生動展示了 BPF技術的實際能力和未來發(fā)展前景。
(掃碼了解本書詳情)
▼掃碼進入本書交流群▼
與廣大群友共同學習成長
![]()
如果喜歡本文 歡迎 在看丨留言丨分享至朋友圈 三連 熱文推薦
▼點擊閱讀原文,獲取本書詳情~
