Top命令--如何排查用戶態(tài) CPU 使用率高?
CPU 的物理核與邏輯核
一臺機(jī)器可能包含多塊 CPU 芯片,多個(gè) CPU 之間通過系統(tǒng)總線通信。一塊 CPU 芯片可能包含多個(gè)物理核,每個(gè)物理核都是一個(gè)實(shí)打?qū)嵉倪\(yùn)算核心(包括運(yùn)算器、存儲器等)。超線程(Hyper-Threading)技術(shù)可以讓一個(gè)物理核在單位時(shí)間內(nèi)同時(shí)處理兩個(gè)線程,變成兩個(gè)邏輯核。但它不會(huì)擁有傳統(tǒng)單核 2 倍的處理能力,也不可能提供完整的并行處理能力。
假設(shè)一個(gè) CPU 芯片就是一個(gè)班級;它有 2 個(gè)物理核,也就是 2 個(gè)同學(xué),老師讓他們分別擔(dān)任班長和體育委員;過了一段時(shí)間,校長要求每個(gè)班級還要有學(xué)習(xí)委員和生活委員,理論上還需要 2 位同學(xué),但是這個(gè)班級只有 2 個(gè)人,最后老師只能讓班長和體育委員兼任。這樣一來,對于不了解的人來說,這個(gè)班級有班長、體育委員、學(xué)習(xí)委員和生活委員 4 個(gè)職位。
top
top 命令輸出
top - 18:31:39 up 158 days, 4:45, 2 users, load average: 2.63, 3.48, 3.53
Tasks: 260 total, 2 running, 258 sleeping, 0 stopped, 0 zombie
%Cpu(s): 38.1 us, 4.2 sy, 0.0 ni, 53.5 id, 2.3 wa, 0.0 hi, 1.9 si, 0.0 st
KiB Mem : 16255048 total, 238808 free, 7608872 used, 8407368 buff/cache
KiB Swap: 33554428 total, 31798304 free, 1756124 used. 7313144 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
32080 root 20 0 8300552 4.125g 11524 S 86.4 26.6 1157:05 java
995 root 20 0 641260 41312 39196 S 28.6 0.3 7420:54 rsyslogd
top 命令找到%CPU 排位最高的進(jìn)程id=32080,進(jìn)而找到對應(yīng)的容器
CPU 使用率就是 CPU 非空閑態(tài)運(yùn)行的時(shí)間占比,比如,單核 CPU 1s 內(nèi)非空閑態(tài)運(yùn)行時(shí)間為 0.8s,那么它的 CPU 使用率就是 80%;雙核 CPU 1s 內(nèi)非空閑態(tài)運(yùn)行時(shí)間分別為 0.4s 和 0.6s,那么,總體 CPU 使用率就是 (0.4s + 0.6s) / (1s * 2) = 50%
%Cpu(s): 38.1 us, 4.2 sy, 0.0 ni, 53.5 id, 2.3 wa, 0.0 hi, 1.9 si, 0.0 st
上述比例加起來是100%
-
us(user):表示 CPU 在用戶態(tài)運(yùn)行的時(shí)間百分比,通常用戶態(tài) CPU 高表示有應(yīng)用程序比較繁忙。典型的用戶態(tài)程序包括:數(shù)據(jù)庫、Web 服務(wù)器等。
-
sy(sys):表示 CPU 在內(nèi)核態(tài)運(yùn)行的時(shí)間百分比(不包括中斷),通常內(nèi)核態(tài) CPU 越低越好,否則表示系統(tǒng)存在某些瓶頸。
-
ni(nice):表示用 nice 修正進(jìn)程優(yōu)先級的用戶態(tài)進(jìn)程執(zhí)行的 CPU 時(shí)間。nice 是一個(gè)進(jìn)程優(yōu)先級的修正值,如果進(jìn)程通過它修改了優(yōu)先級,則會(huì)單獨(dú)統(tǒng)計(jì) CPU 開銷。
-
id(idle):表示 CPU 處于空閑態(tài)的時(shí)間占比,此時(shí),CPU 會(huì)執(zhí)行一個(gè)特定的虛擬進(jìn)程,名為 System Idle Process。
-
wa(iowait):表示 CPU 在等待 I/O 操作完成所花費(fèi)的時(shí)間,通常該指標(biāo)越低越好,否則表示 I/O 存在瓶頸,可以用 iostat 等命令做進(jìn)一步分析。
-
hi(hardirq):表示 CPU 處理硬中斷所花費(fèi)的時(shí)間。硬中斷是由外設(shè)硬件(如鍵盤控制器、硬件傳感器等)發(fā)出的,需要有中斷控制器參與,特點(diǎn)是快速執(zhí)行。
-
si(softirq):表示 CPU 處理軟中斷所花費(fèi)的時(shí)間。軟中斷是由軟件程序(如網(wǎng)絡(luò)收發(fā)、定時(shí)調(diào)度等)發(fā)出的中斷信號,特點(diǎn)是延遲執(zhí)行。
-
st(steal):表示 CPU 被其他虛擬機(jī)占用的時(shí)間,僅出現(xiàn)在多虛擬機(jī)場景。如果該指標(biāo)過高,可以檢查下宿主機(jī)或其他虛擬機(jī)是否異常。
Linux 中 CPU 利用率是如何算出來的?
-
top 命令是讀取的
/proc/stat中輸出的 cpu 各項(xiàng)利用率數(shù)據(jù),而這個(gè)數(shù)據(jù)在內(nèi)核中的是根據(jù) kernel_cpustat(內(nèi)核變量) 來匯總并輸出的。 -
Linux 內(nèi)核每隔固定周期會(huì)發(fā)出 timer interrupt (IRQ 0),每次當(dāng)時(shí)間中斷到來的時(shí)候,都會(huì)調(diào)用 update_process_times 來更新系統(tǒng)時(shí)間。更新后的時(shí)間都存儲在我們前面提到的 percpu 變量 kcpustat_cpu 中。
CPU 使用率與平均負(fù)載的關(guān)系
CPU 使用率是單位時(shí)間內(nèi) CPU 繁忙程度的統(tǒng)計(jì)。而平均負(fù)載不僅包括正在使用 CPU 的進(jìn)程,還包括等待 CPU 或 I/O 的進(jìn)程。因此,兩者不能等同。舉一個(gè)例子:假設(shè)現(xiàn)在有一個(gè)電話亭,有 4 個(gè)人在等待打電話,電話亭同一時(shí)刻只能容納 1 個(gè)人打電話,只有拿起電話筒才算是真正使用。那么 CPU 使用率就是拿起電話筒的時(shí)間占比,它只取決于在電話亭里的人的行為,與平均負(fù)載沒有非常直接的關(guān)系。而平均負(fù)載是指在電話亭里的人加上排隊(duì)的總?cè)藬?shù)。
LinuxLoad Average= 可運(yùn)行隊(duì)列進(jìn)程平均數(shù) + 休眠隊(duì)列中不可打斷的進(jìn)程平均數(shù)
load衡量的是task(linux 內(nèi)核中用于描述一個(gè)進(jìn)程或者線程)對系統(tǒng)的需求(CPU、內(nèi)存、IO等等),system load average由內(nèi)核負(fù)載計(jì)算并記錄在/proc/loadavg 文件中, 用戶態(tài)的工具(比如uptime,top等等)讀的都是這個(gè)文件。內(nèi)核是怎么計(jì)算load average的? 指數(shù)加權(quán)移動(dòng)平均法:a1 = a0 * factor + a * (1 - factor),其中a0是上一時(shí)刻的值,a1是當(dāng)前時(shí)刻的值,factor是一個(gè)系數(shù),取值范圍是[0,1],a是當(dāng)前時(shí)刻的某個(gè)指標(biāo)采樣值。
我們一般認(rèn)為:
-
如果load接近0,意味著系統(tǒng)處于空閑狀態(tài);
-
如果 1min 平均值高于 5min 或 15min 平均值,則負(fù)載正在增加;
-
如果 1min 平均值低于 5min 或 15min 平均值,則負(fù)載正在減少;
-
如果它們高于系統(tǒng) CPU 的數(shù)量,那么系統(tǒng)很可能遇到了性能問題(視情況而定)。
Linux 中的負(fù)載高低和 CPU 開銷并不完全對應(yīng)
如何排查用戶態(tài) CPU 使用率高?
導(dǎo)致load 飆高的原因,說簡單也簡單,無非就是runnable 或者 uninterruptible 的task 增多了。但是說復(fù)雜也復(fù)雜,因?yàn)閷?dǎo)致task進(jìn)入uninterruptible狀態(tài)的路徑非常多(粗略統(tǒng)計(jì),可能有400-500條路徑)。PS:
-
周期性飆高
-
IO原因
-
內(nèi)存原因,比如task 在申請內(nèi)存的時(shí)候,可能會(huì)觸發(fā)內(nèi)存回收,如果觸發(fā)的是直接內(nèi)存回收,那對性能的傷害很大。
-
鎖,比如采用mutex_lock進(jìn)行并發(fā)控制的路徑上,一旦有task 拿著lock 不釋放,其他的task 就會(huì)以TASK_UNINTERRUPTIBLE的狀態(tài)等待,也會(huì)引起load飆高。
-
user CPU,有些情況下load飆高是業(yè)務(wù)的正常表現(xiàn),此時(shí)一般表現(xiàn)為user cpu 飆高
遲分析需要深入內(nèi)核內(nèi)部,在內(nèi)核路徑上埋點(diǎn)取數(shù)。所以這類工具的本質(zhì)是內(nèi)核probe,包括systemtap,kprobe,ebpf等等。但是probe 技術(shù)必須結(jié)合知識和經(jīng)驗(yàn)才能打造成一個(gè)實(shí)用的工具。阿里自研的ali-diagnose可以進(jìn)行各種delay分析,irq_delay, sys_delay, sched_delay, io_delay, load-monitor。
如果想定位消耗 CPU 最多的 Java 代碼,可以遵循如下思路:
-
通過
top命令找到 CPU 消耗最多的進(jìn)程號; -
通過
top -Hp 進(jìn)程號命令找到 CPU 消耗最多的線程號(列名仍然為 PID); -
通過
printf "%x\n" 線程號命令輸出該線程號對應(yīng)的 16 進(jìn)制數(shù)字; -
通過
jstack 進(jìn)程號 | grep 16進(jìn)制線程號 -A 10命令找到 CPU 消耗最多的線程方法堆棧。
如果是非 Java 應(yīng)用,可以將 jstack 替換為 perf。 生產(chǎn)系統(tǒng)推薦使用 APM 產(chǎn)品,比如阿里云的 ARMS,可以自動(dòng)記錄每類線程的 CPU 耗時(shí)和方法棧(并在后臺展示),開箱即用,自動(dòng)保留問題現(xiàn)場
如何限制cpu的使用
CFS Bandwidth Control
The bandwidth allowed for a group(進(jìn)程所屬的組) is specified using a quota and period. Within each given “period” (microseconds), a group is allowed to consume only up to “quota” microseconds of CPU time. When the CPU bandwidth consumption of a group exceeds this limit (for that period), the tasks belonging to its hierarchy will be throttled and are not allowed to run again until the next period. 有幾個(gè)點(diǎn)
-
cpu 不像內(nèi)存 一樣有明確的大小單位,單個(gè)cpu 是獨(dú)占的,只能以cpu 時(shí)間片來衡量。
-
進(jìn)程耗費(fèi)的限制方式:在period(毫秒/微秒) 內(nèi)該進(jìn)程只能占用 quota (毫秒/微秒)。
quota /period = %CPU。PS:內(nèi)存隔離是 申請內(nèi)存的時(shí)候判斷 判斷已申請內(nèi)存有沒有超過閾值。cpu 隔離則是 判斷period周期內(nèi),已耗費(fèi)時(shí)間有沒有超過 quota。PS: 頻控、限流等很多系統(tǒng)也是類似思想 -
period 指的是一個(gè)判斷周期,quota 表示一個(gè)周期內(nèi)可用的多個(gè)cpu的時(shí)間和。 所以quota 可以超過period ,比如period=100 and quota=200,表示在100單位時(shí)間里,進(jìn)程要使用cpu 200單位,需要兩個(gè)cpu 各自執(zhí)行100單位
-
每次拿cpu 說事兒得提兩個(gè)值(period 和 quota)有點(diǎn)麻煩,可以通過進(jìn)程消耗的 CPU 時(shí)間片quota來統(tǒng)計(jì)出進(jìn)程占用 CPU 的百分比。這也是我們看到的各種工具中都使用百分比來說明 CPU 使用率的原因(下文多出有體現(xiàn))。

浙公網(wǎng)安備 33010602011771號