淺談 Linux 高負載的系統化分析
簡介:淺談 Linux 高負載的系統化分析,阿里雲系統組工程師楊勇通過對線上各種問題的系統化分析。
講解 Linux Load 高如何排查的話題屬於老生常談了,但多數文章只是聚焦了幾個點,缺少整體排查思路的介紹。所謂 “授人以魚不如授人以漁”。本文試圖建立一個方法和套路,來幫助讀者對 Load 高問題排查有一個更全面的認識。
從消除誤解開始
沒有基線的 Load,是不靠譜的 Load
從接觸 Unix/Linux 系統管理的第一天起,很多人就開始接觸 System Load Average
這個監控指標了,然而,並非所有人都知道這個指標的真正含義。一般說來,經常能聽到以下誤解:
- Load 高是 CPU 負載高……
傳統 Unix 於 Linux 設計不同。Unix 系統,Load 高就是可執行程序多引發的,但對 Linux 來說不是。對 Linux 來說 Load 高可能有兩種情況:
- 系統中處於
R
狀態的程序數增加引發的 - 系統中處於
D
狀態的程序數增加引發的
- Loadavg 數值大於某個值就一定有問題……
Loadavg 的數值是相對值,受到 CPU 和 IO 裝置多少的影響,甚至會受到某些軟體定義的虛擬資源的影響。Load 高的判斷需要基於某個歷史基線 (Baseline),不能無原則的跨系統去比較 Load。 - Load 高系統一定很忙…..
Load 高系統可以很忙,例如 CPU 負載高,CPU 很忙。但 Load 高,系統不都很忙,如 IO 負載高,磁碟可以很忙,但 CPU 可以比較空閒,如 iowait 高。這裡要注意,iowait 本質上是一種特殊的 CPU 空閒狀態。另一種 Load 高,可能 CPU 和磁碟外設都很空閒,可能支援鎖競爭引起的,這時候 CPU 時間裡,iowait 不高,但 idle 高。
Brendan Gregg 在最近的部落格 [Linux Load Averages: Solving the Mystery] (http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html) 中,討論了 Unix 和 Linux Load Average 的差異,並且回朔到 24 年前 Linux 社群的討論,並找到了當時為什麼 Linux 要修改 Unix Load Average 的定義。文章認為,正是由於 Linux 引入的 D
狀態執行緒的計算方式,從而導致 Load 高的原因變得含混起來。因為系統中引發 D
狀態切換的原因實在是太多了,絕非 IO 負載,鎖競爭這麼簡單!正是由於這種含混,Load 的數值更加難以跨系統,跨應用型別去比較。所有 Load 高低的依據,全都應該基於歷史的基線。本微信公眾號也曾寫過一篇相關文章,可以參見
如何排查 Load 高的問題
如前所述,由於在 Linux 作業系統裡,Load 是一個定義及其含混的指標,排查 loadavg 高就是一個很複雜的過程。其基本思路就是,根據引起 Load 變化的根源是 R
狀態任務增多,還是 D
狀態任務增多,來進入到不同的流程。
這裡給出了 Load 增高的排查的一般套路,僅供參考:
通過簡單區分 R
狀態任務增多,還是 D
狀態任務增多,我們就可以進入到不同的排查流程裡。下面,我們就這個大圖的排查思路,做一個簡單的梳理。
R
狀態任務增多
即通常所說的 CPU 負載高。此類問題的排查定位主要思路是系統,容器,程序的執行時間分析上,找到在 CPU 上的熱點路徑,或者分析 CPU 的執行時間主要是在哪段程式碼上。
CPU user
和 sys
時間的分佈通常能幫助人們快速定位與使用者態程序有關,還是與核心有關。另外,CPU 的 run queue 長度和排程等待時間,非主動的上下文切換 (nonvoluntary context switch) 次數都能幫助大致理解問題的場景。
因此,如果要將問題的場景關聯到相關的程式碼,通常需要使用 perf
,systemtap
, ftrace
這種動態的跟蹤工具。
關聯到程式碼路徑後,接下來的程式碼時間分析過程中,程式碼中的一些無效的執行時間也是分析中首要關注的,例如使用者態和核心態中的自旋鎖 (Spin Lock)。
當然,如果 CPU 上執行的都是有非常意義,非常有效率的程式碼,那唯一要考慮的就是,是不是負載真得太大了。
D
狀態任務增多
根據 Linux 核心的設計, D
狀態任務本質上是 TASK_UNINTERRUPTIBLE
引發的主動睡眠,因此其可能性非常多。但是由於 Linux 核心 CPU 空閒時間上對 IO 棧引發的睡眠做了特殊的定義,即 iowait
,因此iowait
成為 D
狀態分類裡定位是否 Load 高是由 IO 引發的一個重要參考。
當然,如前所述, /proc/stat
中的 procs_blocked
的變化趨勢也可以是一個非常好的判定因 iowait
引發的 Load 高的一個參考。
CPU iowait
高
很多人通常都對 CPU iowait
有一個誤解,以為 iowait
高是因為這時的 CPU 正在忙於做 IO 操作。其實恰恰相反, iowait
高的時候,CPU 正處於空閒狀態,沒有任何任務可以執行。只是因為此時存在已經發出的磁碟 IO,因此這時的空閒狀態被標識成了 iowait
,而不是 idle
。
但此時,如果用 perf probe
命令,我們可以清楚得看到,在 iowait
狀態的 CPU,實際上是執行在 pid 為 0 的 idle 執行緒上:
如前所述,有相當多的核心的阻塞,即 TASK_UNINTERRUPTIBLE
的睡眠,實際上與等待磁碟 IO 無關,如核心中的鎖競爭,再如記憶體直接頁回收的睡眠,又如核心中一些程式碼路徑上的主動阻塞,等待資源。
Brendan Gregg 在最近的部落格 [Linux Load Averages: Solving the Mystery] (http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html)中,使用 perf
命令產生的 TASK_UNINTERRUPTIBLE
的睡眠的火焰圖,很好的展示了引起 CPU idle
高的多樣性。本文不在贅述。
因此,CPU idle
高的分析,實質上就是分析核心的程式碼路徑引起阻塞的主因是什麼。通常,我們可以使用 perf inject
對 perf record
記錄的上下文切換的事件進行處理,關聯出程序從 CPU 切出 (swtich out) 和再次切入 (switch in) 的核心程式碼路徑,生成一個所謂的 Off CPU 火焰圖.
當然,類似於鎖競爭這樣的比較簡單的問題,Off CPU 火焰圖足以一步定位出問題。但是對於更加複雜的因 D
狀態而阻塞的延遲問題,可能 Off CPU 火焰圖只能給我們一個調查的起點。
例如,當我們看到,Off CPU 火焰圖的主要睡眠時間是因為 epoll_wait
等待引發的。那麼,我們繼續要排查的應該是網路棧的延遲,即本文大圖中的 Net Delay 這部分。
至此,你也許會發現,CPU iowait
和 idle
高的效能分析的實質就是 延遲分析
。這就是大圖按照核心中資源管理的大方向,將延遲分析細化成了六大延遲分析:
- CPU 延遲
- 記憶體延遲
- 檔案系統延遲
- IO 棧延遲
- 網路棧延遲
- 鎖及同步原語競爭
任何上述程式碼路徑引發的 TASK_UNINTERRUPTIBLE
的睡眠,都是我們要分析的物件!
以問題結束
限於篇幅,本文很難將其所涉及的細節一一展開,因為讀到這裡,你也許會發現,原來 Load 高的分析,實際上就是對系統的全面負載分析。怪不得叫 System Load 呢。這也是 Load 分析為什麼很難在一篇文章裡去全面覆蓋。
本文也開啟了淺談 Linux 效能分析系列的第一章。後續我們會推出系列文章,就前文所述的六大延遲分析,一一展開介紹,敬請期待……
關於作者
楊勇 (Oliver Yang),Linux 核心工程師,來自阿里雲系統組。曾就職於 EMC,Sun 中國工程研究院,在儲存系統和 Solaris 核心開發領域工作。
本文為阿里雲原創內容,未經允許不得轉載。