[TOC]
源自極客時間教程的學(xué)習(xí)記錄:
通過前兩節(jié)對平均負載和 CPU 上下文切換的學(xué)習(xí)端衰,我相信你對 CPU 的性能已經(jīng)有了初步了解叠洗。不過我還是想問一下,你最常用什么指標(biāo)來描述系統(tǒng)的 CPU 性能呢?我想你的答案旅东,可能不是平均負載灭抑,也不是 CPU 上下文切換,而是另一個更直觀的指標(biāo)—— CPU 使用率抵代。
我們前面說過腾节,CPU 使用率是單位時間內(nèi) CPU 使用情況的統(tǒng)計,以百分比的方式展示荤牍。那么案腺,作為最常用也是最熟悉的 CPU 指標(biāo),你能說出 CPU 使用率到底是怎么算出來的嗎康吵?再有劈榨,諸如 top、ps 之類的性能工具展示的 %user晦嵌、%nice同辣、 %system、%iowait 惭载、%steal 等等旱函,你又能弄清楚它們之間的不同嗎?
今天我就帶你了解 CPU 使用率的內(nèi)容描滔,同時棒妨,我也會以我們最常用的反向代理服務(wù)器 Nginx 為例,帶你在一步步操作和分析中深入理解伴挚。
CPU 使用率
在上一期我曾提到靶衍,Linux 作為一個多任務(wù)操作系統(tǒng),將每個 CPU 的時間劃分為很短的時間片茎芋,再通過調(diào)度器輪流分配給各個任務(wù)使用颅眶,因此造成多任務(wù)同時運行的錯覺。
為了維護 CPU 時間田弥,Linux 通過事先定義的節(jié)拍率(內(nèi)核中表示為 HZ)涛酗,觸發(fā)時間中斷,并使用全局變量 Jiffies 記錄了開機以來的節(jié)拍數(shù)偷厦。每發(fā)生一次時間中斷商叹,Jiffies 的值就加 1。
節(jié)拍率 HZ 是內(nèi)核的可配選項只泼,可以設(shè)置為 100剖笙、250、1000 等请唱。不同的系統(tǒng)可能設(shè)置不同數(shù)值过蹂,你可以通過查詢 /boot/config 內(nèi)核選項來查看它的配置值聚至。比如在我的系統(tǒng)中扳躬,節(jié)拍率設(shè)置成了 250,也就是每秒鐘觸發(fā) 250 次時間中斷击胜。
$ grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=250
同時潜的,正因為節(jié)拍率 HZ 是內(nèi)核選項,所以用戶空間程序并不能直接訪問信不。為了方便用戶空間程序抽活,內(nèi)核還提供了一個用戶空間節(jié)拍率 USER_HZ下硕,它總是固定為 100梭姓,也就是 1/100 秒。這樣誉尖,用戶空間程序并不需要關(guān)心內(nèi)核中 HZ 被設(shè)置成了多少,因為它看到的總是固定值 USER_HZ琢感。
Linux 通過 /proc 虛擬文件系統(tǒng)探熔,向用戶空間提供了系統(tǒng)內(nèi)部狀態(tài)的信息,而 /proc/stat 提供的就是系統(tǒng)的 CPU 和任務(wù)統(tǒng)計信息柬甥。比方說,如果你只關(guān)注 CPU 的話喜滨,可以執(zhí)行下面的命令:
# 只保留各個CPU的數(shù)據(jù)
$ cat /proc/stat | grep ^cpu
cpu 280580 7407 286084 172900810 83602 0 583 0 0 0
cpu0 144745 4181 176701 86423902 52076 0 301 0 0 0
cpu1 135834 3226 109383 86476907 31525 0 282 0 0 0
這里的輸出結(jié)果是一個表格虽风。其中辜膝,第一列表示的是 CPU 編號厂抖,如 cpu0忱辅、cpu1 墙懂,而第一行沒有編號的 cpu 损搬,表示的是所有 CPU 的累加柜与。其他列則表示不同場景下 CPU 的累加節(jié)拍數(shù),它的單位是 USER_HZ弄匕,也就是 10 ms(1/100 秒)颅悉,所以這其實就是不同場景下的 CPU 時間粘茄。
當(dāng)然签舞,這里每一列的順序并不需要你背下來柒瓣。你只要記住儒搭,有需要的時候芙贫,查詢 man proc 就可以搂鲫。不過,你要清楚 man proc 文檔里每一列的涵義磺平,它們都是 CPU 使用率相關(guān)的重要指標(biāo)魂仍,你還會在很多其他的性能工具中看到它們。下面擦酌,我來依次解讀一下赊舶。
- user(通痴龃睿縮寫為 us),代表用戶態(tài) CPU 時間寓调。注意,它不包括下面的 nice 時間,但包括了 guest 時間痛悯。
- nice(通辰ヅ牛縮寫為 ni),代表低優(yōu)先級用戶態(tài) CPU 時間灸蟆,也就是進程的 nice 值被調(diào)整為 1-19 之間時的 CPU 時間。這里注意亲族,nice 可取值范圍是 -20 到 19炒考,數(shù)值越大,優(yōu)先級反而越低霎迫。
- system(通痴啵縮寫為 sys),代表內(nèi)核態(tài) CPU 時間知给。
- idle(通橙恐悖縮寫為 id),代表空閑時間涩赢。注意戈次,它不包括等待 I/O 的時間(iowait)。
- iowait(通惩舶牵縮寫為 wa)怯邪,代表等待 I/O 的 CPU 時間。
- irq(通郴ǘ眨縮寫為 hi)悬秉,代表處理硬中斷的 CPU 時間澄步。
- softirq(通常縮寫為 si)和泌,代表處理軟中斷的 CPU 時間村缸。
- guest(通常縮寫為 guest)武氓,代表通過虛擬化運行其他操作系統(tǒng)的時間梯皿,也就是運行虛擬機的 CPU 時間。
而我們通常所說的 CPU 使用率聋丝,就是除了空閑時間外的其他時間占總 CPU 時間的百分比索烹,用公式來表示就是:
根據(jù)這個公式,我們就可以從 /proc/stat 中的數(shù)據(jù)弱睦,很容易地計算出 CPU 使用率百姓。當(dāng)然,也可以用每一個場景的 CPU 時間况木,除以總的 CPU 時間垒拢,計算出每個場景的 CPU 使用率。
不過先不要著急計算火惊,你能說出求类,直接用 /proc/stat 的數(shù)據(jù),算的是什么時間段的 CPU 使用率嗎屹耐?
看到這里尸疆,你應(yīng)該想起來了,這是開機以來的節(jié)拍數(shù)累加值惶岭,所以直接算出來的寿弱,是開機以來的平均 CPU 使用率,一般沒啥參考價值按灶。
事實上症革,為了計算 CPU 使用率,性能工具一般都會取間隔一段時間(比如 3 秒)的兩次值鸯旁,作差后噪矛,再計算出這段時間內(nèi)的平均 CPU 使用率,即
這個公式铺罢,就是我們用各種性能工具所看到的 CPU 使用率的實際計算方法艇挨。
現(xiàn)在,我們知道了系統(tǒng) CPU 使用率的計算方法韭赘,那進程的呢雷袋?跟系統(tǒng)的指標(biāo)類似,Linux 也給每個進程提供了運行情況的統(tǒng)計信息,也就是 /proc/[pid]/stat楷怒。不過蛋勺,這個文件包含的數(shù)據(jù)就比較豐富了,總共有 52 列的數(shù)據(jù)鸠删。
當(dāng)然抱完,不用擔(dān)心,因為你并不需要掌握每一列的含義刃泡。還是那句話巧娱,需要的時候,查 man proc 就行烘贴。
回過頭來看禁添,是不是說要查看 CPU 使用率,就必須先讀取 /proc/stat 和 /proc/[pid]/stat 這兩個文件桨踪,然后再按照上面的公式計算出來呢老翘?
當(dāng)然不是,各種各樣的性能分析工具已經(jīng)幫我們計算好了锻离。不過要注意的是铺峭,性能分析工具給出的都是間隔一段時間的平均 CPU 使用率,所以要注意間隔時間的設(shè)置汽纠,特別是用多個工具對比分析時卫键,你一定要保證它們用的是相同的間隔時間。
比如虱朵,對比一下 top 和 ps 這兩個工具報告的 CPU 使用率莉炉,默認(rèn)的結(jié)果很可能不一樣,因為 top 默認(rèn)使用 3 秒時間間隔碴犬,而 ps 使用的卻是進程的整個生命周期呢袱。
怎么查看 CPU 使用率
知道了 CPU 使用率的含義后,我們再來看看要怎么查看 CPU 使用率翅敌。說到查看 CPU 使用率的工具,我猜你第一反應(yīng)肯定是 top 和 ps惕蹄。的確蚯涮,top 和 ps 是最常用的性能分析工具:
- top 顯示了系統(tǒng)總體的 CPU 和內(nèi)存使用情況,以及各個進程的資源使用情況卖陵。
- ps 則只顯示了每個進程的資源使用情況遭顶。
比如,top 的輸出格式為:
# 默認(rèn)每3秒刷新一次
$ top
top - 11:58:59 up 9 days, 22:47, 1 user, load average: 0.03, 0.02, 0.00
Tasks: 123 total, 1 running, 72 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8169348 total, 5606884 free, 334640 used, 2227824 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 7497908 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 78088 9288 6696 S 0.0 0.1 0:16.83 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.05 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H
...
這個輸出結(jié)果中泪蔫,第三行 %Cpu 就是系統(tǒng)的 CPU 使用率棒旗,具體每一列的含義上一節(jié)都講過,只是把 CPU 時間變換成了 CPU 使用率,我就不再重復(fù)講了铣揉。不過需要注意饶深,top 默認(rèn)顯示的是所有 CPU 的平均值,這個時候你只需要按下數(shù)字 1 逛拱,就可以切換到每個 CPU 的使用率了敌厘。
繼續(xù)往下看,空白行之后是進程的實時信息朽合,每個進程都有一個 %CPU 列俱两,表示進程的 CPU 使用率。它是用戶態(tài)和內(nèi)核態(tài) CPU 使用率的總和曹步,包括進程用戶空間使用的 CPU宪彩、通過系統(tǒng)調(diào)用執(zhí)行的內(nèi)核空間 CPU 、以及在就緒隊列等待運行的 CPU讲婚。在虛擬化環(huán)境中尿孔,它還包括了運行虛擬機占用的 CPU。
所以磺樱,到這里我們可以發(fā)現(xiàn)纳猫, top 并沒有細分進程的用戶態(tài) CPU 和內(nèi)核態(tài) CPU。那要怎么查看每個進程的詳細情況呢竹捉?你應(yīng)該還記得上一節(jié)用到的 pidstat 吧芜辕,它正是一個專門分析每個進程 CPU 使用情況的工具。
比如块差,下面的 pidstat 命令侵续,就間隔 1 秒展示了進程的 5 組 CPU 使用率,包括:
- 用戶態(tài) CPU 使用率 (%usr)
- 內(nèi)核態(tài) CPU 使用率(%system)
- 運行虛擬機 CPU 使用率(%guest)
- 等待 CPU 使用率(%wait)
- 以及總的 CPU 使用率(%CPU)憨闰。
最后的 Average 部分状蜗,還計算了 5 組數(shù)據(jù)的平均值。
# 每隔1秒輸出一組數(shù)據(jù)鹉动,共輸出5組
$ pidstat 1 5
15:56:02 UID PID %usr %system %guest %wait %CPU CPU Command
15:56:03 0 15006 0.00 0.99 0.00 0.00 0.99 1 dockerd
...
Average: UID PID %usr %system %guest %wait %CPU CPU Command
Average: 0 15006 0.00 0.99 0.00 0.00 0.99 - dockerd
CPU 使用率過高怎么辦轧坎?
通過 top、ps泽示、pidstat 等工具缸血,你能夠輕松找到 CPU 使用率較高(比如 100% )的進程。接下來械筛,你可能又想知道捎泻,占用 CPU 的到底是代碼里的哪個函數(shù)呢?找到它埋哟,你才能更高效笆豁、更針對性地進行優(yōu)化。
那么哪種工具適合在第一時間分析進程的 CPU 問題呢?我的推薦是 perf闯狱。perf 是 Linux 2.6.31 以后內(nèi)置的性能分析工具煞赢。它以性能事件采樣為基礎(chǔ),不僅可以分析系統(tǒng)的各種事件和內(nèi)核性能扩氢,還可以用來分析指定應(yīng)用程序的性能問題耕驰。
使用 perf 分析 CPU 性能問題,我來說兩種最常見录豺、也是我最喜歡的用法朦肘。
第一種常見用法是 perf top,類似于 top双饥,它能夠?qū)崟r顯示占用 CPU 時鐘最多的函數(shù)或者指令媒抠,因此可以用來查找熱點函數(shù),使用界面如下所示:
$ perf top
Samples: 833 of event 'cpu-clock', Event count (approx.): 97742399
Overhead Shared Object Symbol
7.28% perf [.] 0x00000000001f78a4
4.72% [kernel] [k] vsnprintf
4.32% [kernel] [k] module_get_kallsym
3.65% [kernel] [k] _raw_spin_unlock_irqrestore
...
輸出結(jié)果中咏花,第一行包含三個數(shù)據(jù)趴生,分別是采樣數(shù)(Samples)、事件類型(event)和事件總數(shù)量(Event count)昏翰。比如這個例子中苍匆,perf 總共采集了 833 個 CPU 時鐘事件,而總事件數(shù)則為 97742399棚菊。
另外浸踩,采樣數(shù)需要我們特別注意。如果采樣數(shù)過少(比如只有十幾個)统求,那下面的排序和百分比就沒什么實際參考價值了检碗。
再往下看是一個表格式樣的數(shù)據(jù),每一行包含四列码邻,分別是:
- 第一列 Overhead 折剃,是該符號的性能事件在所有采樣中的比例,用百分比來表示
- 第二列 Shared 像屋,是該函數(shù)或指令所在的動態(tài)共享對象(Dynamic Shared Object)怕犁,如內(nèi)核、進程名己莺、動態(tài)鏈接庫名奏甫、內(nèi)核模塊名
- 第三列 Object ,是動態(tài)共享對象的類型 比如 [.] 表示用戶空間的可執(zhí)行程序篇恒、或者動態(tài)鏈接庫,而 [k] 則表示內(nèi)核空間
- 最后一列 Symbol 是符號名凶杖,也就是函數(shù)名當(dāng)函數(shù)名未知時胁艰,用十六進制的地址來表示
還是以上面的輸出為例,我們可以看到,占用 CPU 時鐘最多的是 perf 工具自身腾么,不過它的比例也只有 7.28%奈梳,說明系統(tǒng)并沒有 CPU 性能問題。 perf top 的使用你應(yīng)該很清楚了吧解虱。
接著再來看第二種常見用法攘须,也就是 perf record 和 perf report。 perf top 雖然實時展示了系統(tǒng)的性能信息殴泰,但它的缺點是并不保存數(shù)據(jù)于宙,也就無法用于離線或者后續(xù)的分析。而 perf record 則提供了保存數(shù)據(jù)的功能悍汛,保存后的數(shù)據(jù)捞魁,需要你用 perf report 解析展示。
$ perf record # 按Ctrl+C終止采樣
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.452 MB perf.data (6093 samples) ]
$ perf report # 展示類似于perf top的報告
在實際使用中离咐,我們還經(jīng)常為 perf top 和 perf record 加上 -g 參數(shù)谱俭,開啟調(diào)用關(guān)系的采樣,方便我們根據(jù)調(diào)用鏈來分析性能問題宵蛀。
小結(jié)
CPU 使用率是最直觀和最常用的系統(tǒng)性能指標(biāo)昆著,更是我們在排查性能問題時,通常會關(guān)注的第一個指標(biāo)术陶。所以我們更要熟悉它的含義凑懂,尤其要弄清楚用戶(%user)、Nice(%nice)瞳别、系統(tǒng)(%system) 征候、等待 I/O(%iowait) 、中斷(%irq)以及軟中斷(%softirq)這幾種不同 CPU 的使用率祟敛。比如說:
- 用戶 CPU 和 Nice CPU 高疤坝,說明用戶態(tài)進程占用了較多的 CPU,所以應(yīng)該著重排查進程的性能問題馆铁。
- 系統(tǒng) CPU 高跑揉,說明內(nèi)核態(tài)占用了較多的 CPU,所以應(yīng)該著重排查內(nèi)核線程或者系統(tǒng)調(diào)用的性能問題埠巨。
- I/O 等待 CPU 高历谍,說明等待 I/O 的時間比較長,所以應(yīng)該著重排查系統(tǒng)存儲是不是出現(xiàn)了 I/O 問題辣垒。
- 軟中斷和硬中斷高望侈,說明軟中斷或硬中斷的處理程序占用了較多的 CPU,所以應(yīng)該著重排查內(nèi)核中的中斷服務(wù)程序勋桶。
碰到 CPU 使用率升高的問題脱衙,你可以借助 top侥猬、pidstat 等工具,確認(rèn)引發(fā) CPU 性能問題的來源捐韩;再使用 perf 等工具退唠,排查出引起性能問題的具體函數(shù)。