CPU效能排查總結

2020-08-09 14:34:30

CPU效能排查總結

重要指標說明

平均負載

使用uptime命令檢視平均負載結果如下

# uptime
 04:13:44 up 3 days, 1 min,  2 users,  load average: 0.05, 0.05, 0.05
# 其中load average:0.05, 0.05, 0.05
# 分別表示過去1分鐘,5分鐘,15分鐘的平均負載
# man uptime解釋如下:
# System load averages is the average number of processes that are either in a runnable or uninterruptable state.  A process in
# a  runnable  state is either using the CPU or waiting to use the CPU.  A process in uninterruptable state is waiting for some
# I/O access, eg waiting for disk.  The averages are taken over the three time intervals.  Load averages are not normalized for
# the number of CPUs in a system, so a load average of 1 means a single CPU system is loaded all the time while on a 4 CPU sys‐
# tem it means it was idle 75% of the time.

平均負載指的是單位時間內,系統處於可執行狀態不可中斷狀態的平均進程數,也就是平均活躍進程數。可執行狀態指的是正在使用CPU或正在等待CPU的進程,也就是ps命令中檢視的處於R(Running或Runnable)狀態的進程,不可中斷狀態的進程則處於內核態關鍵流程中的進程,並且這些流程是不可打斷的,比如常見的等待硬體裝置的IO響應,也就是ps命令檢視的D(Uninterruptible Sleep,也成爲Disk Sleep)狀態,不可中斷狀態實際上是系統對進程和硬體裝置的一種保護機制 機製。

CPU使用率

CPU使用類別有:

  • user(通常縮寫爲 us),代表使用者態 CPU 時間。注意,它不包括下面 下麪的 nice 時間,但包括了 guest 時間。
  • nice(通常縮寫爲 ni),代表低優先順序使用者態 CPU 時間,也就是進程的 nice 值被調整爲 1-19 之間時的 CPU 時間。這裏注意,nice 可取值範圍是 -20 到 19,數值越大,優先順序反而越低。
  • system(通常縮寫爲 sys),代表內核態 CPU 時間。idle(通常縮寫爲 id),代表空閒時間。注意,它不包括等待 I/O 的時間(iowait)。
  • iowait(通常縮寫爲 wa),代表等待 I/O 的 CPU 時間。
  • irq(通常縮寫爲 hi),代表處理硬中斷的 CPU 時間。
  • softirq(通常縮寫爲 si),代表處理軟中斷的 CPU 時間。
  • steal(通常縮寫爲 st),代表當系統執行在虛擬機器中的時候,被其他虛擬機器佔用的 CPU 時間。
  • guest(通常縮寫爲 guest),代表通過虛擬化執行其他操作系統的時間,也就是執行虛擬機器的 CPU 時間。
  • guest_nice(通常縮寫爲 gnice),代表以低優先順序執行虛擬機器的時間。

CPU使用率就是除了空閒時間外的其他時間佔總CPU時間的百分比:
cpu使=1/cpu cpu使用率=1-空閒時間/總cpu時間

平均負載指的是單位時間內,處於可執行狀態和不可執行狀態的進程數,所以不僅包括了正在使用CPU的進程,還包括等待CPU和等待I/O的進程,而CPU使用率是單位時間內CPU繁忙情況的統計,跟平均負載不一定完全對應。如:

  • CPU密集型,使用大量CPU資源導致平均負載升高,此時兩者是一致的
  • I/O密集型,等待I/O會導致平均負載升高,但CPU使用率不一定很高
  • 大量等待CPU的進程排程也會導致平均負載升高,此時CPU使用率也會較高

CPU 使用率是最直觀和最常用的系統效能指標,更是我們在排查效能問題時,通常會關注的第一個指標。所以我們更要熟悉它的含義,尤其要弄清楚使用者(%user)、Nice(%nice)、系統(%system) 、等待 I/O(%iowait) 、中斷(%irq)以及軟中斷(%softirq)這幾種不同 CPU 的使用率。比如說:

  • 使用者 CPU 和 Nice CPU 高,說明使用者態進程佔用了較多的 CPU,所以應該着重排查進程的效能問題。
  • 系統 CPU 高,說明內核態佔用了較多的 CPU,所以應該着重排查內核執行緒或者系統呼叫的效能問題。
  • I/O 等待 CPU 高,說明等待 I/O 的時間比較長,所以應該着重排查系統儲存是不是出現了 I/O 問題。
  • 軟中斷和硬中斷高,說明軟中斷或硬中斷的處理程式佔用了較多的 CPU,所以應該着重排查內核中的中斷服務程式。

碰到 CPU 使用率升高的問題,你可以藉助 top、pidstat 等工具,確認引發 CPU 效能問題的來源;再使用 perf 等工具,排查出引起效能問題的具體函數

CPU上下文切換

Linux是一個多工操作系統,它支援遠大於CPU數量的任務同時執行,這些任務並非真正同時執行,而是因爲系統在很短時間內,將CPU輪流分配給他們,造成多工同時執行的錯覺。而在每個任務執行前,需要系統事先幫它設定好CPU暫存器和程式計數器(Program Counter,PC)

CPU暫存器是CPU內建的容量很小,但是速度極快的記憶體,而程式計數器則是用來儲存CPU正在執行的指令位置,或即將執行的下一條指令位置,他們都是CPU在執行任何任務前必須依賴的環境,因此也叫CPU上下文

CPU上下文切換就是把前一個任務的CPU上下文(CPU暫存器和程式計數器)儲存起來,然後載入新任務的上下文到這些暫存器和程式計數器,最後再跳轉到程式計數器所指的新位置,執行新任務

根據任務的不同CPU上下文切換可以分爲幾個不同的場景,也就是進程上下文切換執行緒上下文切換以及中斷上下文切換

不管是那種上下文切換,需要注意的是:

  • CPU 上下文切換,是保證 Linux 系統正常工作的核心功能之一,一般情況下不需要我們特別關注。
  • 過多的上下文切換,會把 CPU 時間消耗在暫存器、內核棧以及虛擬記憶體等數據的儲存和恢復上,從而縮短進程真正執行的時間,導致系統的整體效能大幅下降。

進程上下文切換

Linux按照特權等級,把進程的執行空間分爲內核空間和使用者空間,分別對應CPU特權等級的Ring 0 和 Ring 3。

  • 內核空間(Ring 0)具有最高許可權,可以直接存取所有資源
  • 使用者空間(Ring 3)只能存取受限資源,不能直接存取記憶體等硬體裝置,必須通過系統呼叫陷入到內核中,才能 纔能存取這些特權資源

也就是進程可以在使用者空間執行,也可以在內核空間執行,進程在使用者空間執行時,稱爲進程的使用者態,陷入內核空間的時候,被稱爲進程的內核態

從使用者態到內核態的切換需要通過系統呼叫來完成,比如當檢視檔案內容時,就需要多次系統呼叫來完成,先呼叫open()開啓檔案,然後呼叫read()讀取檔案內容,並呼叫write()將內容寫到標準輸出,最後再呼叫close檔案

而系統呼叫的過程中會將CPU暫存器裡原來使用者態的指令位置儲存起來,接着爲了執行內核態程式碼,CPU暫存器需要更新爲內核態指令的新位置,最後再跳轉到內核態執行內核任務。

系統呼叫結束後,CPU暫存器需要恢復原來儲存的使用者態,再切換到使用者空間,繼續執行進程,所以,一次系統呼叫,實際上發生了2此CPU上下文切換

不過,需要注意的是,系統呼叫過程中,並不會涉及到虛擬記憶體等進程使用者態的資源,也不會切換進程。這跟我們通常所說的進程上下文切換是不一樣的:

  • 進程上下文切換,是指從一個進程切換到另一個進程執行。
  • 而系統呼叫過程中一直是同一個進程在執行

所以,**系統呼叫過程通常稱爲特權模式切換,而不是上下文切換。**但實際上,系統呼叫過程中,CPU 的上下文切換還是無法避免的。

進程是由內核來管理和排程的,進程的切換隻能發生在內核態。所以,進程的上下文不僅包括了虛擬記憶體、棧、全域性變數等使用者空間的資源,還包括了內核堆疊、暫存器等內核空間的狀態。

因此,進程的上下文切換就比系統呼叫時多了一步:在儲存當前進程的內核狀態和 CPU 暫存器之前,需要先把該進程的虛擬記憶體、棧等儲存下來;而載入了下一進程的內核態後,還需要重新整理進程的虛擬記憶體和使用者棧。

儲存上下文和恢復上下文並不是免費的需要內核再CPU上執行才能 纔能完成

每次上下文切換都需要幾十納秒到數微秒的 CPU 時間。這個時間還是相當可觀的,特別是在進程上下文切換次數較多的情況下,很容易導致 CPU 將大量時間耗費在暫存器、內核棧以及虛擬記憶體等資源的儲存和恢復上,進而大大縮短了真正執行進程的時間。

另外,我們知道, Linux 通過 TLB(Translation Lookaside Buffer)來管理虛擬記憶體到實體記憶體的對映關係。當虛擬記憶體更新後,TLB 也需要重新整理,記憶體的存取也會隨之變慢。特別是在多處理器系統上,快取是被多個處理器共用的,重新整理快取不僅會影響當前處理器的進程,還會影響共用快取的其他處理器的進程。

進程切換時才需要切換上下文,換句話說,只有在進程排程的時候,才需要切換上下文。Linux 爲每個 CPU 都維護了一個就緒佇列,將活躍進程(即正在執行和正在等待 CPU 的進程)按照優先順序和等待 CPU 的時間排序,然後選擇最需要 CPU 的進程,也就是優先順序最高和等待 CPU 時間最長的進程來執行。

觸發進程排程到CPU執行的場景:

  1. 爲了保證所有進程可以得到公平排程,CPU 時間被劃分爲一段段的時間片,這些時間片再被輪流分配給各個進程。這樣,當某個進程的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的進程執行。
  2. 進程在系統資源不足(比如記憶體不足)時,要等到資源滿足後纔可以執行,這個時候進程也會被掛起,並由系統排程其他進程執行。
  3. 當進程通過睡眠函數 sleep 這樣的方法將自己主動掛起時,自然也會重新排程。
  4. 當有優先順序更高的進程執行時,爲了保證高優先順序進程的執行,當前進程會被掛起,由高優先順序進程來執行。
  5. 發生硬體中斷時,CPU 上的進程會被中斷掛起,轉而執行內核中的中斷服務程式。

執行緒上下文切換

執行緒與進程最大的區別在於,執行緒是排程的基本單位,而進程則是資源擁有的基本單位。所謂內核中的任務排程,實際上的排程物件是執行緒;而進程只是給執行緒提供了虛擬記憶體、全域性變數等資源。

  • 當進程只有一個執行緒,可以認爲進程等於執行緒
  • 當進程有多個執行緒,這些執行緒會共用相同的虛擬記憶體和全域性變數等資源,這些資源再上下文切換時是不需要修改的
  • 執行緒也有自己的私有數據,這些在上下文切換時是需要儲存的。

當執行緒發生上下文切換就分爲兩種情況:

  1. 屬於不同進程,此時由於資源不共用,所以切換過程就跟進程上下文切換是一樣的
  2. 屬於同一個進程,此時虛擬記憶體是共用的,在切換時,虛擬記憶體這些資源不動,只需要切換執行緒的私有數據,暫存器等不共用的數據

同爲上下文切換,可以看出同進程內的執行緒切換要比多進程間的切換消耗更少的資源,這也是多執行緒代替多進程的一個優勢

中斷上下文切換

爲了快速響應硬體事件,中斷程式會打斷進程的正常排程和執行,轉而呼叫中斷處理程式,響應裝置事件,而在打斷其他進程時,就需要將進程當前的狀態儲存下來,這樣在中斷結束後,進程仍然可以從原來的狀態中恢復執行,跟進程上下文不同,中斷上下文切換不涉及進程的使用者態,所以中斷過程打斷了一個正處於使用者態的進程,也不需要儲存和恢復這個進程的虛擬記憶體,全域性變數等使用者態資源,中斷上下文,其實只包括內核態終端服務程式執行所必須的狀態,包括CPU暫存器,內核堆疊,硬體中斷等參數。

對同一個 CPU 來說,中斷處理比進程擁有更高的優先順序,所以中斷上下文切換並不會與進程上下文切換同時發生。同樣道理,由於中斷會打斷正常進程的排程和執行,所以大部分中斷處理程式都短小精悍,以便儘可能快的執行結束。

另外,跟進程上下文切換一樣,中斷上下文切換也需要消耗 CPU,切換次數過多也會耗費大量的 CPU,甚至嚴重降低系統的整體效能。所以,當你發現中斷次數過多時,就需要注意去排查它是否會給你的系統帶來嚴重的效能問題。

cswch與nvcswch

  • cswch ,表示每秒自願上下文切換(voluntary context switches)的次數,

  • nvcswch ,表示每秒非自願上下文切換(non voluntary context switches)的次數。

這兩個指標可以通過pidstat -w檢視,其中:

  • 自願上下文切換,是指進程無法獲取所需資源,導致的上下文切換。比如說, I/O、記憶體等系統資源不足時,就會發生自願上下文切換。
  • 非自願上下文切換,則是指進程由於時間片已到等原因,被系統強制排程,進而發生的上下文切換。比如說,大量進程都在爭搶 CPU 時,就容易發生非自願上下文切換。

關於上下文切換的瓶頸分析:

  • 自願上下文切換變多了,說明進程都在等待資源,有可能發生了 I/O 等其他問題;
  • 非自願上下文切換變多了,說明進程都在被強制排程,也就是都在爭搶 CPU,說明 CPU 的確成了瓶頸;
  • 中斷次數變多了,說明 CPU 被中斷處理程式佔用,還需要通過檢視watch -d "cat /proc/interrupts" 檔案來分析具體的中斷型別。

軟中斷

中斷其實是一種非同步的事件處理機制 機製,可以提高系統的併發處理能力。由於中斷處理程式會打斷其他進程的執行,所以,爲了減少對正常進程執行排程的影響,中斷處理程式就需要儘可能快地執行。如果中斷本身要做的事情不多,那麼處理起來也不會有太大問題;但如果中斷要處理的事情很多,中斷服務程式就有可能要執行很長時間。特別是,中斷處理程式在響應中斷時,還會臨時關閉中斷。這就會導致上一次中斷處理完成之前,其他中斷都不能響應,也就是說中斷有可能會丟失。

Linux將中斷處理過程分爲上半部和下半部:

  • 上半部用來快速處理中斷,它在中斷禁止模式下執行,主要處理跟硬體緊密相關的或時間敏感的工作。
  • 下半部用來延遲處理上半部未完成的工作,通常以內核執行緒的方式執行。

例如:網絡卡接收到數據包後,會通過硬體中斷的方式,通知內核有新的數據到了。這時,內核就應該呼叫中斷處理程式來響應它。對上半部來說,既然是快速處理,其實就是要把網絡卡的數據讀到記憶體中,然後更新一下硬體暫存器的狀態(表示數據已經讀好了),最後再發送一個軟中斷信號,通知下半部做進一步的處理。而下半部被軟中斷信號喚醒後,需要從記憶體中找到網路數據,再按照網路協定棧,對數據進行逐層解析和處理,直到把它送給應用程式。

所以,這兩個階段你也可以這樣理解:

  • 上半部直接處理硬體請求,也就是我們常說的硬中斷,特點是快速執行;
  • 下半部則是由內核觸發,也就是我們常說的軟中斷,特點是延遲執行。

實際上,上半部會打斷 CPU 正在執行的任務,然後立即執行中斷處理程式。而下半部以內核執行緒的方式執行,並且每個 CPU 都對應一個軟中斷內核執行緒,名字爲 「ksoftirqd/CPU 編號」,比如說, 0 號 CPU 對應的軟中斷內核執行緒的名字就是 ksoftirqd/0。不過要注意的是,軟中斷不只包括了剛剛所講的硬體裝置中斷處理程式的下半部,一些內核自定義的事件也屬於軟中斷,比如內核排程和 RCU 鎖(Read-Copy Update 的縮寫,RCU 是 Linux 內核中最常用的鎖之一)等。

檢視中斷資訊:

  • /proc/softirqs 提供了軟中斷的執行情況,Linux 中的軟中斷包括網路收發、定時、排程、RCU 鎖等各種型別
  • /proc/interrupts 提供了硬中斷的執行情況。
# cat /proc/softirqs  | awk '{print $1,$2,$3,$4} '
CPU0 CPU1 CPU2 CPU3
HI: 0 2 0
TIMER: 52776094 26302251 0
NET_TX: 54647 3 0
NET_RX: 3299726 839641 0
BLOCK: 241695 358027 0
BLOCK_IOPOLL: 0 0 0
TASKLET: 10020 103 0
SCHED: 6887846 4866827 0
HRTIMER: 0 0 0
RCU: 7585671 5658226 0
# 第一,要注意軟中斷的型別,也就是這個介面中第一列的內容。從第一列你可以看到,軟中斷包括了 10 個類別,分別對應不同的工作型別。比如 NET_RX 表示網路接收中斷,而 NET_TX 表示網路發送中斷。
# 第二,要注意同一種軟中斷在不同 CPU 上的分佈情況,也就是同一行的內容。正常情況下,同一種中斷在不同 CPU 上的累積次數應該差不多。比如這個介面中,NET_RX 在 CPU0 和 CPU1 上的中斷次數基本是同一個數量級,相差不大。

CPU效能分析常用工具

  • mpstat:常用的多核 CPU 效能分析工具,用來實時檢視每個 CPU 的效能指標,以及所有 CPU 的平均指標。

  • pidstat:一個常用的進程效能分析工具,用來實時檢視進程的 CPU、記憶體、I/O 以及上下文切換等效能指標。

  • uptime:檢視系統平均負載,可以結合pidstat,mpstat進一步分析排查

  • vmstat:檢視上下文切換和中斷次數異常,可以結合pidstat進一步排查

  • perf top/record/report -g:檢視系統和進程的CPU使用狀況

  • execsnoop:監控進程呼叫,系統預設沒有

  • dstat:檢視系統整體狀態,可以結合pidstat,strace,perf進一步排查

  • sar -n DEV 1:檢視網路情況,後續可以再結合tcpdump進一步排查

  • watch -d cat /proc/softirqs || cat /proc/interrupts:監控中斷狀態

  • pstree:檢視進程之間的父子關係

mpstat

mpstat 是一個常用的多核 CPU 效能分析工具,用來實時檢視每個 CPU 的效能指標,以及所有 CPU 的平均指標。

# 1. 顯示所有CPU的指標,並在間隔5秒輸出一組數據
# mpstat -P ALL 5 1
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     07/29/2020      _x86_64_        (2 CPU)

04:34:45 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
04:34:50 AM  all    0.30    0.00    0.60    0.00    0.00    0.00    0.00    0.00    0.00   99.09
04:34:50 AM    0    0.20    0.00    0.60    0.00    0.00    0.20    0.00    0.00    0.00   99.00
04:34:50 AM    1    0.40    0.00    0.80    0.00    0.00    0.00    0.00    0.00    0.00   98.80

Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
Average:     all    0.30    0.00    0.60    0.00    0.00    0.00    0.00    0.00    0.00   99.09
Average:       0    0.20    0.00    0.60    0.00    0.00    0.20    0.00    0.00    0.00   99.00
Average:       1    0.40    0.00    0.80    0.00    0.00    0.00    0.00    0.00    0.00   98.80
# 輸出完畢後會計算這段時間內的平均值,也就是Average



pidstat

pidstat 是一個常用的進程效能分析工具,用來實時檢視進程的 CPU、記憶體、I/O 以及上下文切換等效能指標。

# 1. 間隔5秒後輸出一組數據
# pidstat -u 5 1
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     07/29/2020      _x86_64_        (2 CPU)

04:36:32 AM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
04:36:37 AM     0      2607    0.00    0.20    0.00    0.20     1  sshd
04:36:37 AM     0      2628    0.00    0.40    0.00    0.40     1  bash
04:36:37 AM  1000     30315    0.40    0.20    0.00    0.60     1  mysqld
04:36:37 AM     0    114958    0.20    0.00    0.00    0.20     0  pidstat

Average:      UID       PID    %usr %system  %guest    %CPU   CPU  Command
Average:        0      2607    0.00    0.20    0.00    0.20     -  sshd
Average:        0      2628    0.00    0.40    0.00    0.40     -  bash
Average:     1000     30315    0.40    0.20    0.00    0.60     -  mysqld
Average:        0    114958    0.20    0.00    0.00    0.20     -  pidstat
# 輸出完畢後會計算這段時間內的平均值,也就是Average
# 2. w檢視上下文切換相關數據,t表示輸出執行緒資訊,預設只顯示進程資訊
# pidstat -wt 5 1
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     07/29/2020      _x86_64_        (2 CPU)

06:02:08 AM   UID      TGID       TID   cswch/s nvcswch/s  Command
06:02:13 AM     0         3         -      1.31      0.00  ksoftirqd/0
06:02:13 AM     0         -         3      1.31      0.00  |__ksoftirqd/0
06:02:13 AM     0         7         -      2.43      0.00  migration/0
06:02:13 AM     0         -         7      2.43      0.00  |__migration/0
06:02:13 AM     0         9         -     40.67      0.00  rcu_sched
...

Average:      UID      TGID       TID   cswch/s nvcswch/s  Command
Average:        0         3         -      1.31      0.00  ksoftirqd/0
Average:        0         -         3      1.31      0.00  |__ksoftirqd/0
Average:        0         7         -      2.43      0.00  migration/0
Average:        0         -         7      2.43      0.00  |__migration/0
Average:        0         9         -     40.67      0.00  rcu_sched
Average:        0         -         9     36.01      0.00  |__rcu_sched
...

# UID: The real user identification number of the task being monitored. 
# USER: The name of the real user owning the task being monitored. 
# PID: The identification number of the task being monitored.
# cswch/s: Total  number of voluntary context switches the task made per second.  A voluntary context switch occurs when a task blocks because it requires a resource that is unavailable.表示每秒自願上下文切換(voluntary context switches)的次數
# nvcswch/s: Total number of non voluntary context switches the task made per second.  A involuntary  context  switch  takes place when a task executes for the duration of its time slice and then is forced to relinquish the processor.非自願上下文切換,則是指進程由於時間片已到等原因,被系統強制排程,進而發生的上下文切換。
# Command:The command name of the task.

# -d 展示 I/O 統計數據,-p 指定進程號,間隔 1 秒輸出 3 組數據
# pidstat -d -p 4344 1 3
06:38:50      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:38:51        0      4344      0.00      0.00      0.00       0  app
06:38:52        0      4344      0.00      0.00      0.00       0  app
06:38:53        0      4344      0.00      0.00      0.00       0  app
# kB_rd/s:Number of kilobytes the task has caused to be read from disk per second.每秒讀的 KB 數
# kB_wr/s:Number of kilobytes the task has caused, or shall cause to be written to disk per second.每秒寫的 KB 數
# kB_ccwr/s:Number  of  kilobytes  whose writing to disk has been cancelled by the task. This may occur when the task truncates some dirty pagecache. In this case, some IO which another task has been accounted for will not be happening.任務已取消對磁碟的寫入的千字節數。當任務truncates一些髒頁面時,可能會發生這種情況。在這種情況下,其他任務已經考慮的一些IO將不會發生。

vmstat

vmstat 是一個常用的系統效能分析工具,主要用來分析系統的記憶體使用情況,也常用來分析 CPU 上下文切換和中斷的次數。

# 1. 每隔5秒輸出1組數據
# vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0  19712 158884      0 2959688    0    0     6    31   71   51 11  1 88  0  0
 0  0  19712 158984      0 2959704    0    0     0     0  258  413  0  1 99  0  0
 0  0  19712 159020      0 2959704    0    0     0     0  251  413  0  1 99  0  0
 0  0  19712 158876      0 2959704    0    0     0     0  258  420  0  1 99  0  0
 0  0  19712 158504      0 2959704    0    0     0     0  261  420  0  1 99  0  0
# 其中第一組數據是系統執行至今的平均數據
# 第二組開始時每隔5s的系統資源情況
#   Procs
#       r: The number of runnable processes (running or waiting for run time).正在執行和等待 CPU 的進程數。
#       b: The number of processes in uninterruptible sleep.處於不可中斷睡眠進程數
#   Memory
#       swpd: the amount of virtual memory used.已被使用的虛擬記憶體
#       free: the amount of idle memory.空閒記憶體
#       buff: the amount of memory used as buffers.被用於buffer的記憶體
#       cache: the amount of memory used as cache.被用於cache的記憶體
#   Swap
#       si: Amount of memory swapped in from disk (/s).
#       so: Amount of memory swapped to disk (/s).
#   IO
#       bi: Blocks received from a block device (blocks/s).
#       bo: Blocks sent to a block device (blocks/s).
#   System
#       in: The number of interrupts per second, including the clock.每秒中斷數
#       cs: The number of context switches per second.每秒上下文切換數
#   CPU
#       These are percentages of total CPU time.
#       us: Time spent running non-kernel code.  (user time, including nice time)
#       sy: Time spent running kernel code.  (system time)
#       id: Time spent idle.  Prior to Linux 2.5.41, this includes IO-wait time.
#       wa: Time spent waiting for IO.  Prior to Linux 2.5.41, included in idle.
#       st: Time stolen from a virtual machine.  Prior to Linux 2.6.11, unknown.

perf

perf 是 Linux 2.6.31 以後內建的效能分析工具。它以效能事件採樣爲基礎,不僅可以分析系統的各種事件和內核效能,還可以用來分析指定應用程式的效能問題。

# -g表示開啓呼叫關係採樣
# perf top -g
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
...
# 輸出結果中,第一行包含三個數據,分別是採樣數(Samples)、事件型別(event)和事件總數量(Event count)。
# 比如這個例子中,perf 總共採集了 833 個 CPU 時鐘事件,而總事件數則爲 97742399。
# 第一列 Overhead ,是該符號的效能事件在所有採樣中的比例,用百分比來表示。
# 第二列 Shared ,是該函數或指令所在的動態共用物件(Dynamic Shared Object),如內核、進程名、動態鏈接庫名、內核模組名等。
# 第三列 Object ,是動態共用物件的型別。比如 [.] 表示使用者空間的可執行程式、或者動態鏈接庫,而 [k] 則表示內核空間。
# 最後一列 Symbol 是符號名,也就是函數名。當函數名未知時,用十六進制的地址來表示。

# perf record 則提供了儲存數據的功能
# perf record -g # 按Ctrl+C終止採樣
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.452 MB perf.data (6093 samples) ]
# # 展示類似於perf top的報告
# perf report 

pstree

pstree 就可以用樹狀形式顯示所有進程之間的關係

# pstree | grep stress
        |-docker-containe-+-php-fpm-+-php-fpm---sh---stress
        |         |-3*[php-fpm---sh---stress---stress]

top

常用的檢視進程狀態的工具

top - 07:42:47 up 3 days,  3:30,  3 users,  load average: 0.37, 0.34, 0.18
Tasks: 127 total,   1 running, 126 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.2 us,  0.9 sy,  0.0 ni, 98.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3861512 total,   120708 free,   836384 used,  2904420 buff/cache
KiB Swap:  2097148 total,  2077436 free,    19712 used.  2704060 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  2607 root      20   0  158856   6164   4732 S   4.3  0.2   5:45.27 sshd
 50252 root      20   0  113180   1588   1332 S   4.3  0.0   0:00.79 bash
 49990 root      20   0  319260  11780   4780 S   2.7  0.3   0:11.46 urlgrabber-ext-
 30315 mysql     20   0 1810792 620164   9112 S   1.0 16.1  53:35.23 mysqld
 49194 root      20   0  583032  27888   9604 S   0.3  0.7   0:11.04 yum
     1 root      20   0  128148   4332   2680 S   0.0  0.1   1:02.07 systemd
# S 列(也就是 Status 列)表示進程的狀態,有如下選項:
# |-- R 是 Running 或 Runnable 的縮寫,表示進程在 CPU 的就緒佇列中,正在執行或者正在等待執行。
# |-- D 是 Disk Sleep 的縮寫,也就是不可中斷狀態睡眠(Uninterruptible Sleep),一般表示進程正在跟硬體互動,並且互動過程不允許被其他進程或中斷打斷。
# |-- Z 是 Zombie 的縮寫,表示殭屍進程,也就是進程實際上已經結束了,但是父進程還沒有回收它的資源(比如進程的描述符、PID 等)。
# |-- S 是 Interruptible Sleep 的縮寫,也就是可中斷狀態睡眠,表示進程因爲等待某個事件而被系統掛起。當進程等待的事件發生時,它會被喚醒並進入 R 狀態。
# |-- I 是 Idle 的縮寫,也就是空閒狀態,用在不可中斷睡眠的內核執行緒上。前面說了,硬體互動導致的不可中斷進程用 D 表示,但對某些內核執行緒來說,它們有可能實際上並沒有任何負載,用 Idle 正是爲了區分這種情況。要注意,D 狀態的進程會導致平均負載升高, I 狀態的進程卻不會。
# |-- T 或者 t,也就是 Stopped 或 Traced 的縮寫,表示進程處於暫停或者跟蹤狀態。向一個進程發送 SIGSTOP 信號,它就會因響應這個信號變成暫停狀態(Stopped);再向它發送 SIGCONT 信號,進程又會恢復執行(如果進程是終端裡直接啓動的,則需要你用 fg 命令,恢復到前臺執行)。而當你用偵錯程式(如 gdb)偵錯一個進程時,在使用斷點中斷進程後,進程就會變成跟蹤狀態,這其實也是一種特殊的暫停狀態,只不過你可以用偵錯程式來跟蹤並按需要控制進程的執行。
# |-- X,也就是 Dead 的縮寫,表示進程已經消亡,所以你不會在 top 或者 ps 命令中看到它。

這裏補充一個孤兒進程和殭屍進程的區別

  • 孤兒進程:一個父進程退出,而它的一個或多個子進程還在執行,那麼那些子進程將成爲孤兒進程。孤兒進程將被init進程(進程號爲1)所收養,並由init進程對它們完成狀態收集工作。

  • 殭屍進程:正常情況下,當一個進程建立了子進程後,它應該通過系統呼叫 wait()或者 waitpid() 等待子進程結束,回收子進程的資源;而子進程在結束時,會向它的父進程發送 SIGCHLD 信號,所以,父進程還可以註冊 SIGCHLD 信號的處理常式,非同步回收資源。如果父進程沒這麼做,或是子進程執行太快,父進程還沒來得及處理子進程狀態,子進程就已經提前退出,那這時的子進程就會變成殭屍進程。換句話說,父親應該一直對兒子負責,善始善終,如果不作爲或者跟不上,都會導致「問題少年」的出現。通常,殭屍進程持續的時間都比較短,在父進程回收它的資源後就會消亡;或者在父進程退出後,由 init 進程回收後也會消亡。一旦父進程沒有處理子進程的終止,還一直保持執行狀態,那麼子進程就會一直處於殭屍狀態。大量的殭屍進程會用盡 PID 進程號,導致新進程不能建立,所以這種情況一定要避免。

ps

檢視當前程式的快照資訊


# ps aux | grep /app
root      4009  0.0  0.0   4376  1008 pts/0    Ss+  05:51   0:00 /app
root      4287  0.6  0.4  37280 33660 pts/0    D+   05:54   0:00 /app
root      4288  0.6  0.4  37280 33668 pts/0    D+   05:54   0:00 /app
# S 表示可中斷睡眠狀態,
# D 表示不可中斷睡眠狀態,我們在前面剛學過,
# s 表示這個進程是一個對談的領導進程
# + 表示前臺行程羣組。
# 行程羣組表示一組相互關聯的進程,比如每個子進程都是父進程所在組的成員;而對談是指共用同一個控制終端的一個或多個行程羣組。

dstat

可以同時檢視 CPU 和 I/O 這兩種資源的使用情況

# 間隔1秒輸出10組數據
# dstat 1 10
You did not select any stats, using -cdngy by default.
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai stl| read  writ| recv  send|  in   out | int   csw
  0   0  96   4   0|1219k  408k|   0     0 |   0     0 |  42   885
  0   0   2  98   0|  34M    0 | 198B  790B|   0     0 |  42   138
  0   0   0 100   0|  34M    0 |  66B  342B|   0     0 |  42   135
  0   0  84  16   0|5633k    0 |  66B  342B|   0     0 |  52   177
  0   3  39  58   0|  22M    0 |  66B  342B|   0     0 |  43   144
  0   0   0 100   0|  34M    0 | 200B  450B|   0     0 |  46   147
  0   0   2  98   0|  34M    0 |  66B  342B|   0     0 |  45   134
  0   0   0 100   0|  34M    0 |  66B  342B|   0     0 |  39   131
  0   0  83  17   0|5633k    0 |  66B  342B|   0     0 |  46   168
  0   3  39  59   0|  22M    0 |  66B  342B|   0     0 |  37   134

sar

sar 可以用來檢視系統的網路收發情況,還有一個好處是,不僅可以觀察網路收發的吞吐量(BPS,每秒收發的位元組數),還可以觀察網路收發的 PPS,即每秒收發的網路幀數。

# -n DEV 表示顯示網路收發的報告,間隔1秒輸出一組數據
$ sar -n DEV 1
15:03:46        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
15:03:47         eth0  12607.00   6304.00    664.86    358.11      0.00      0.00      0.00      0.01
15:03:47      docker0   6302.00  12604.00    270.79    664.66      0.00      0.00      0.00      0.00
15:03:47           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
15:03:47    veth9f6bbcd   6302.00  12604.00    356.95    664.66      0.00      0.00      0.00      0.05
# 第一列:表示報告的時間。
# 第二列:IFACE 表示網絡卡。
# 第三、四列:rxpck/s 和 txpck/s 分別表示每秒接收、發送的網路幀數,也就是  PPS。
# 第五、六列:rxkB/s 和 txkB/s 分別表示每秒接收、發送的千字節數,也就是  BPS。

CPU排查套路

  • 根據指標找工具
    image
    • 工具對應的指標
      image
      • 效能分析路徑圖
        image

參考文件

  • 《極客時間:Linux效能優化實戰》,作者:倪鵬飛