一裆悄、實驗環(huán)境
二、實驗案例分析
安裝完成后臂聋,我們先在第一個終端光稼,執(zhí)行下面的命令運行案例,也就是一個最基本的 Nginx 應(yīng)用:
?運行 Nginx 服務(wù)并對外開放 80 端口
# docker run -itd --name=nginx -p 80:80? nginx
然后逻住,在第二個終端钟哥,使用 curl 訪問 Nginx 監(jiān)聽的端口迎献,確認 Nginx 正常啟動瞎访。
假設(shè) 192.168.0.30 是 Nginx 所在虛擬機的 IP 地址,運行 curl 命令后吁恍,你應(yīng)該會看到下面這個輸出界面:
#??curl http://192.168.0.30/
接著扒秸,還是在第二個終端中,運行 hping3 命令冀瓦,模擬 Nginx 的客戶端請求:
#? hping3 -S -p 80 -i u10 192.168.0.30
現(xiàn)在伴奥,我們再回到第一個終端,你應(yīng)該就會發(fā)現(xiàn)異骋砻觯——系統(tǒng)的響應(yīng)明顯變慢了拾徙。
我們不妨執(zhí)行 top,觀察一下系統(tǒng)和進程的 CPU 使用情況:
從 top 的輸出中感局,你可以看到尼啡,兩個 CPU 的軟中斷使用率都超過了 30%;而 CPU 使用率最高的進程询微,正好是軟中斷內(nèi)核線程 ksoftirqd/0 和 ksoftirqd/1崖瞭。
雖然,我們已經(jīng)知道了 ksoftirqd 的基本功能撑毛,可以猜測是因為大量網(wǎng)絡(luò)收發(fā)书聚,引起了 CPU 使用率升高;但它到底在執(zhí)行什么邏輯,我們卻并不知道雌续。
對于普通進程斩个,我們要觀察其行為有很多方法,比如 strace驯杜、pstack萨驶、lsof 等等。但這些工具并不適合內(nèi)核線程艇肴,比如腔呜,如果你用 pstack ,或者通過 /proc/pid/stack 查看 ksoftirqd/0(進程號為 9)的調(diào)用棧時再悼,分別可以得到以下輸出:
那還有沒有其他方法核畴,來觀察內(nèi)核線程 ksoftirqd 的行為呢?
既然是內(nèi)核線程冲九,自然應(yīng)該用到內(nèi)核中提供的機制谤草。回顧一下我們之前用過的 CPU 性能工具莺奸,我想你肯定還記得 perf 丑孩,這個內(nèi)核自帶的性能剖析工具。
perf 可以對指定的進程或者事件進行采樣灭贷,并且還可以用調(diào)用棧的形式温学,輸出整個調(diào)用鏈上的匯總信息。?
我們不妨就用 perf 甚疟,來試著分析一下進程號為 9 的 ksoftirqd仗岖。
#? perf record -a -g -p 9 -- sleep 30
#################################################################
# man perf
# man 1 perf-record
perf-record - Run a command and record its profile into perf.data
-a, --all-cpus? ? ? ? System-wide collection from all CPUs (default if no target is specified).
-g? ? ? ? ? ? ? ? ? ? ? ? ? Enables call-graph (stack chain/backtrace) recording.
-p, --pid=? ? ? ?? ? ? Record events on existing process ID (comma separated list).
##################################################################
稍等一會兒,在上述命令結(jié)束后览妖,繼續(xù)執(zhí)行?perf report命令轧拄,你就可以得到 perf 的匯總報告。
按上下方向鍵以及回車鍵讽膏,展開比例最高的 ksoftirqd 后檩电,你就可以得到下面這個調(diào)用關(guān)系鏈圖:
從這個圖中,你可以清楚看到 ksoftirqd 執(zhí)行最多的調(diào)用過程府树。
雖然你可能不太熟悉內(nèi)核源碼俐末,但通過這些函數(shù),我們可以大致看出它的調(diào)用棧過程挺尾。
我們的猜測對不對呢鹅搪?
實際上,我們案例最開始用 Docker 啟動了容器遭铺,而 Docker 會自動為容器創(chuàng)建虛擬網(wǎng)卡丽柿、橋接到 docker0 網(wǎng)橋并配置 NAT 規(guī)則恢准。
這一過程,如下圖所示:
當(dāng)然了甫题,前面 perf report 界面的調(diào)用鏈還可以繼續(xù)展開馁筐。
不幸的是我的屏幕不夠大,如果展開更多的層級坠非,最后幾個層級會超出屏幕范圍敏沉。
這樣,即使我們能看到大部分的調(diào)用過程炎码,卻也不能說明后面層級就沒問題盟迟。
那么,有沒有更好的方法潦闲,來查看整個調(diào)用棧的信息呢攒菠?
火焰圖
針對 perf 匯總數(shù)據(jù)的展示問題,Brendan Gragg 發(fā)明了火焰圖歉闰,通過矢量圖的形式命斧,更直觀展示匯總結(jié)果贱除。
下圖就是一個針對 MySQL 的火焰圖示例衫生。
這張圖看起來像是跳動的火焰采桃,因此也就被稱為火焰圖。
要理解火焰圖昼弟,我們最重要的是區(qū)分清楚橫軸和縱軸的含義:
上面 MySQL 火焰圖的示例啤它,就表示了 CPU 的繁忙情況,這種火焰圖也被稱為 on-CPU 火焰圖私杜。
如果我們根據(jù)性能分析的目標來劃分蚕键,火焰圖可以分為下面這幾種:
了解了火焰圖的含義和查看方法后救欧,接下來衰粹,我們再回到案例,運用火焰圖來觀察剛才 perf record 得到的記錄笆怠。
火焰圖分析
首先铝耻,我們需要生成火焰圖。我們先下載幾個能從 perf record 記錄生成火焰圖的工具蹬刷,這些工具都放在?https://github.com/brendangregg/FlameGraph?上面瓢捉。
你可以執(zhí)行下面的命令來下載:
#? git clone https://github.com/brendangregg/FlameGraph
#? cd FlameGraph
安裝好工具后,要生成火焰圖办成,其實主要需要三個步驟:
執(zhí)行 perf script 泡态,將 perf record 的記錄轉(zhuǎn)換成可讀的采樣記錄;
執(zhí)行 stackcollapse-perf.pl 腳本迂卢,合并調(diào)用棧信息某弦;
執(zhí)行 flamegraph.pl 腳本桐汤,生成火焰圖。
不過靶壮,在 Linux 中怔毛,我們可以使用管道,來簡化這三個步驟的執(zhí)行過程腾降。
假設(shè)剛才用 perf record 生成的文件路徑為 /root/perf.data拣度,執(zhí)行下面的命令,你就可以直接生成火焰圖:
# perf script -i? /root/perf.data | ./stackcollapse-perf.pl --all | ./flamegraph.pl > ksoftirqd.svg
執(zhí)行成功后螃壤,使用瀏覽器打開 ksoftirqd.svg 抗果,你就可以看到生成的火焰圖了。
如下圖所示:
根據(jù)剛剛講過的火焰圖原理奸晴,這個圖應(yīng)該從下往上看窖张,沿著調(diào)用棧中最寬的函數(shù)來分析執(zhí)行次數(shù)最多的函數(shù)。
這兒看到的結(jié)果蚁滋,其實跟剛才的 perf report 類似宿接,但直觀了很多,中間這一團火辕录,很明顯就是最需要我們關(guān)注的地方睦霎。
我們順著調(diào)用棧由下往上看(順著圖中藍色箭頭),就可以得到跟剛才 perf report 中一樣的結(jié)果:
不過最后走诞,到了 ip_forward 這里副女,已經(jīng)看不清函數(shù)名稱了。
所以我們需要點擊 ip_forward蚣旱,展開最上面這一塊調(diào)用棧:
這樣碑幅,就可以進一步看到 ip_forward 后的行為,也就是把網(wǎng)絡(luò)包發(fā)送出去塞绿。根據(jù)這個調(diào)用過程沟涨,再結(jié)合我們前面學(xué)習(xí)的網(wǎng)絡(luò)收發(fā)和 TCP/IP 協(xié)議棧原理,這個流程中的網(wǎng)絡(luò)接收异吻、網(wǎng)橋以及 netfilter 調(diào)用等裹赴,都是導(dǎo)致軟中斷 CPU 升高的重要因素,也就是影響網(wǎng)絡(luò)性能的潛在瓶頸诀浪。
不過棋返,回想一下網(wǎng)絡(luò)收發(fā)的流程,你可能會覺得它缺了好多步驟雷猪。
比如睛竣,這個堆棧中并沒有 TCP 相關(guān)的調(diào)用,也沒有連接跟蹤 conntrack 相關(guān)的函數(shù)求摇。實際上射沟,這些流程都在其他更小的火焰中嫉你,你可以點擊上圖左上角的“Reset Zoom”,回到完整火焰圖中躏惋,再去查看其他小火焰的堆棧幽污。
所以,在理解這個調(diào)用棧時要注意簿姨,從任何一個點出發(fā)距误、縱向來看的整個調(diào)用棧,其實只是最頂端那一個函數(shù)的調(diào)用堆棧扁位,而非完整的內(nèi)核網(wǎng)絡(luò)執(zhí)行流程准潭。
另外,整個火焰圖不包含任何時間的因素域仇,所以并不能看出橫向各個函數(shù)的執(zhí)行次序刑然。
到這里,我們就找出了內(nèi)核線程 ksoftirqd 執(zhí)行最頻繁的函數(shù)調(diào)用堆棧暇务,而這個堆棧中的各層級函數(shù)泼掠,就是潛在的性能瓶頸來源。這樣垦细,后面想要進一步分析择镇、優(yōu)化時,也就有了根據(jù)括改。
當(dāng)時腻豌,我們從軟中斷 CPU 使用率的角度入手,用網(wǎng)絡(luò)抓包的方法找出了瓶頸來源嘱能,確認是測試機器發(fā)送的大量 SYN 包導(dǎo)致的吝梅。而通過今天的 perf 和火焰圖方法,我們進一步找出了軟中斷內(nèi)核線程的熱點函數(shù)惹骂,其實也就找出了潛在的瓶頸和優(yōu)化方向苏携。
其實,如果遇到的是內(nèi)核線程的資源使用異常析苫,很多常用的進程級性能工具并不能幫上忙兜叨。這時,你就可以用內(nèi)核自帶的 perf 來觀察它們的行為衩侥,找出熱點函數(shù),進一步定位性能瓶矛物。當(dāng)然茫死,perf 產(chǎn)生的匯總報告并不夠直觀,所以我也推薦你用火焰圖來協(xié)助排查履羞。
實際上峦萎,火焰圖方法同樣適用于普通進程屡久。比如,在分析 Nginx爱榔、MySQL 等各種應(yīng)用場景的性能問題時被环,火焰圖也能幫你更快定位熱點函數(shù),找出潛在性能問題详幽。
CPU火焰圖和內(nèi)存火焰圖筛欢,在生成數(shù)據(jù)時有什么不同?
CPU 火焰圖和內(nèi)存火焰圖唇聘,最大的差別其實就是數(shù)據(jù)來源的不同版姑,也就是函數(shù)堆棧不同,而火焰圖的格式還是完全一樣的迟郎。
火焰圖的結(jié)構(gòu)是一樣的剥险,只是函數(shù)堆棧不一樣,內(nèi)存火焰圖側(cè)重于內(nèi)存管理函數(shù)的調(diào)用棧宪肖。
內(nèi)存火焰圖生成perf.data數(shù)據(jù)時表制,perf record加哪些選項?
要加上內(nèi)存管理相關(guān)的事件(函數(shù))控乾,比如 perf record -e syscalls:sys_enter_mmap -a -g -- sleep 60
三夫凸、參考
內(nèi)核線程 CPU 利用率太高,我該怎么辦阱持?
https://time.geekbang.org/column/article/86330
SVG 圖像入門教程
https://www.ruanyifeng.com/blog/2018/08/svg.html
SVG 簡介
https://www.runoob.com/svg/svg-intro.html
如何讀懂火焰圖夭拌?
http://www.ruanyifeng.com/blog/2017/09/flame-graph.html
Linux程序性能分析和火焰圖
https://www.cnblogs.com/pugang/p/10659301.html
Linux下用火焰圖進行性能分析
https://blog.csdn.net/gatieme/article/details/78885908
火焰圖介紹
https://book.aikaiyuan.com/linux/www.lijiaocn.com/linux/chapter1/04-flame-graphs.html
火焰圖性能分析 perf
https://www.jevic.cn/2019/03/03/perf
perf + 火焰圖分析程序性能
https://www.cnblogs.com/happyliu/p/6142929.html
使用 Perf 和火焰圖分析 CPU 性能
http://senlinzhan.github.io/2018/03/18/perf
利用火焰圖分析程序性能瓶頸
https://blog.angelmsger.com/%E5%88%A9%E7%94%A8%E7%81%AB%E7%84%B0%E5%9B%BE%E5%88%86%E6%9E%90%E7%A8%8B%E5%BA%8F%E6%80%A7%E8%83%BD%E7%93%B6%E9%A2%88