講解 Linux Load 高如何排查的話題屬于老生常談了,但多數(shù)文章只是聚焦了幾個點,缺少整體排查思路的介紹。所謂“授人以魚不如授人以漁"。本文試圖建立一個方法和套路,來幫助讀者對 Load 高問題排查有一個更全面的認(rèn)識。
從消除誤解開始
沒有基線的 Load,是不靠譜的 Load
從接觸 Unix/Linux 系統(tǒng)管理的第一天起,很多人就開始接觸System Load Average這個監(jiān)控指標(biāo)了,然而,并非所有人都知道這個指標(biāo)的真正含義。一般說來,經(jīng)常能聽到以下誤解: - Load 高是 CPU 負(fù)載高......
傳統(tǒng) Unix 于 Linux 設(shè)計不同。Unix 系統(tǒng),Load 高就是可運行進(jìn)程多引發(fā)的,但對 Linux 來說不是。對 Linux 來說 Load 高可能有兩種情況: - 系統(tǒng)中處于R狀態(tài)的進(jìn)程數(shù)增加引發(fā)的 - 系統(tǒng)中處于D狀態(tài)的進(jìn)程數(shù)增加引發(fā)的 - Loadavg 數(shù)值大于某個值就一定有問題......
Loadavg 的數(shù)值是相對值,受到 CPU 和 IO 設(shè)備多少的影響,甚至?xí)艿侥承┸浖x的虛擬資源的影響。Load 高的判斷需要基于某個歷史基線 (Baseline),不能無原則的跨系統(tǒng)去比較 Load。 - Load 高系統(tǒng)一定很忙.....
Load 高系統(tǒng)可以很忙,例如 CPU 負(fù)載高,CPU 很忙。但 Load 高,系統(tǒng)不都很忙,如 IO 負(fù)載高,磁盤可以很忙,但 CPU 可以比較空閑,如 iowait 高。這里要注意,iowait 本質(zhì)上是一種特殊的 CPU 空閑狀態(tài)。另一種 Load 高,可能 CPU 和磁盤外設(shè)都很空閑,可能支持鎖競爭引起的,這時候 CPU 時間里,iowait 不高,但 idle 高。
Brendan Gregg 在最近的博客 [Linux Load Averages: Solving the Mystery] (http://www.brendangregg.com/blog/2017-08-08/linux-load-averages.html, Brendan Gregg: Linux Load Averages究竟是個什么鬼?) 中,討論了 Unix 和 Linux Load Average 的差異,并且回朔到 24 年前 Linux 社區(qū)的討論,并找到了當(dāng)時為什么 Linux 要修改 Unix Load Average 的定義。文章認(rèn)為,正是由于 Linux 引入的D狀態(tài)線程的計算方式,從而導(dǎo)致 Load 高的原因變得含混起來。因為系統(tǒng)中引發(fā)D狀態(tài)切換的原因?qū)嵲谑翘嗔耍^非 IO 負(fù)載,鎖競爭這么簡單!正是由于這種含混,Load 的數(shù)值更加難以跨系統(tǒng),跨應(yīng)用類型去比較。所有 Load 高低的依據(jù),全都應(yīng)該基于歷史的基線。本文無意過多搬運原文的內(nèi)容,因此,進(jìn)一步的細(xì)節(jié),建議閱讀原文。
如何排查 Load 高的問題
如前所述,由于在 Linux 操作系統(tǒng)里,Load 是一個定義及其含混的指標(biāo),排查 loadavg 高就是一個很復(fù)雜的過程。其基本思路就是,根據(jù)引起 Load 變化的根源是R狀態(tài)任務(wù)增多,還是D狀態(tài)任務(wù)增多,來進(jìn)入到不同的流程。
這里給出了 Load 增高的排查的一般套路,僅供參考:
在 Linux 系統(tǒng)里,讀取/proc/stat文件,即可獲取系統(tǒng)中R狀態(tài)的進(jìn)程數(shù);但D狀態(tài)的任務(wù)數(shù)恐怕最直接的方式還是使用ps命令比較方便。而/proc/stat文件里procs_blocked則給出的是處于等待磁盤 IO 的進(jìn)程數(shù):
$cat /proc/stat .......processes 50777849procs_running 1procs_blocked 0......
通過簡單區(qū)分R狀態(tài)任務(wù)增多,還是D狀態(tài)任務(wù)增多,我們就可以進(jìn)入到不同的排查流程里。下面,我們就這個大圖的排查思路,做一個簡單的梳理。
R狀態(tài)任務(wù)增多
即通常所說的 CPU 負(fù)載高。此類問題的排查定位主要思路是系統(tǒng),容器,進(jìn)程的運行時間分析上,找到在 CPU 上的熱點路徑,或者分析 CPU 的運行時間主要是在哪段代碼上。
CPUuser和sys時間的分布通常能幫助人們快速定位與用戶態(tài)進(jìn)程有關(guān),還是與內(nèi)核有關(guān)。另外,CPU 的 run queue 長度和調(diào)度等待時間,非主動的上下文切換 (nonvoluntary context switch) 次數(shù)都能幫助大致理解問題的場景。
因此,如果要將問題的場景關(guān)聯(lián)到相關(guān)的代碼,通常需要使用perf,systemtap,ftrace這種動態(tài)的跟蹤工具。
關(guān)聯(lián)到代碼路徑后,接下來的代碼時間分析過程中,代碼中的一些無效的運行時間也是分析中首要關(guān)注的,例如用戶態(tài)和內(nèi)核態(tài)中的自旋鎖 (Spin Lock)。
當(dāng)然,如果 CPU 上運行的都是有非常意義,非常有效率的代碼,那唯一要考慮的就是,是不是負(fù)載真得太大了。
D狀態(tài)任務(wù)增多
根據(jù) Linux 內(nèi)核的設(shè)計,D狀態(tài)任務(wù)本質(zhì)上是TASK_UNINTERRUPTIBLE引發(fā)的主動睡眠,因此其可能性非常多。但是由于 Linux 內(nèi)核 CPU 空閑時間上對 IO 棧引發(fā)的睡眠做了特殊的定義,即iowait,因此iowait成為D狀態(tài)分類里定位是否 Load 高是由 IO 引發(fā)的一個重要參考。
當(dāng)然,如前所述,/proc/stat中的procs_blocked的變化趨勢也可以是一個非常好的判定因iowait引發(fā)的 Load 高的一個參考。
CPUiowait高
很多人通常都對 CPUiowait有一個誤解,以為iowait高是因為這時的 CPU 正在忙于做 IO 操作。其實恰恰相反,iowait高的時候,CPU 正處于空閑狀態(tài),沒有任何任務(wù)可以運行。只是因為此時存在已經(jīng)發(fā)出的磁盤 IO,因此這時的空閑狀態(tài)被標(biāo)識成了iowait,而不是idle。
但此時,如果用perf probe命令,我們可以清楚得看到,在iowait狀態(tài)的 CPU,實際上是運行在 pid 為 0 的 idle 線程上:
$ sudo perf probe -a account_idle_ticks$sudo perf record -e probe:account_idle_ticks -ag sleep 1[ perf record: Woken up 1 times to write data ][ perf record: Captured and wrote 0.418 MB perf.data (843 samples) ]$sudo perf scriptswapper 0 [013] 5911414.451891: probe:account_idle_ticks: (ffffffff810b6af0) 2b6af1 account_idle_ticks (/lib/modules/3.10.0/build/vmlinux) 2d65d9 cpu_startup_entry (/lib/modules/3.10.0/build/vmlinux) 24840a start_secondary (/lib/modules/3.10.0/build/vmlinux)
相關(guān)的 idle 線程的循環(huán)如何分別對 CPUiowait和idle計數(shù)的代碼,如下所示:
/*
* Account multiple ticks of idle time.
* @ticks: number of stolen ticks
*/
void account_idle_ticks(unsigned long ticks)
{
if (sched_clock_irqtime) {
irqtime_account_idle_ticks(ticks);
return;
}
account_idle_time(jiffies_to_cputime(ticks));
}
/*
* Account for idle time.
* @cputime: the cpu time spent in idle wait
*/
void account_idle_time(cputime_t cputime)
{
u64 *cpustat = kcpustat_this_cpu->cpustat;
struct rq *rq = this_rq();
if (atomic_read(&rq->nr_iowait) > 0)
cpustat[CPUTIME_IOWAIT] += (__force u64) cputime;
else
cpustat[CPUTIME_IDLE] += (__force u64) cputime;
}
而 Linux IO 棧和文件系統(tǒng)的代碼則會調(diào)用io_schedule,等待磁盤 IO 的完成。這時候,對 CPU 時間被記為iowait起關(guān)鍵計數(shù)的原子變量rq->nr_iowait則會在睡眠前被增加。注意,io_schedule 在被調(diào)用前,通常 caller 會先將任務(wù)顯式地設(shè)置成TASK_UNINTERRUPTIBLE狀態(tài):
/*
* This task is about to go to sleep on IO. Increment rq->nr_iowait so
* that process accounting knows that this is a task in IO wait state.
*/
void __sched io_schedule(void)
{
io_schedule_timeout(MAX_SCHEDULE_TIMEOUT);
}
EXPORT_SYMBOL(io_schedule);
long __sched io_schedule_timeout(long timeout)
{
int old_iowait = current->in_iowait;
struct rq *rq;
long ret;
current->in_iowait = 1;
if (old_iowait)
blk_schedule_flush_plug(current);
else
blk_flush_plug(current);
delayacct_blkio_start();
rq = raw_rq();
atomic_inc(&rq->nr_iowait);
ret = schedule_timeout(timeout);
current->in_iowait = old_iowait;
atomic_dec(&rq->nr_iowait);
delayacct_blkio_end();
return ret;
}
EXPORT_SYMBOL(io_schedule_timeout);
CPUidle高
如前所述,有相當(dāng)多的內(nèi)核的阻塞,即 TASK_UNINTERRUPTIBLE 的睡眠,實際上與等待磁盤 IO 無關(guān),如內(nèi)核中的鎖競爭,再如內(nèi)存直接頁回收的睡眠,又如內(nèi)核中一些代碼路徑上的主動阻塞,等待資源。
Brendan Gregg 在最近的博客 [Linux Load Averages: Solving the Mystery] 中,使用 perf 命令產(chǎn)生的 TASK_UNINTERRUPTIBLE 的睡眠的火焰圖,很好的展示了引起 CPU idle 高的多樣性。本文不在贅述。
因此,CPU idle 高的分析,實質(zhì)上就是分析內(nèi)核的代碼路徑引起阻塞的主因是什么。通常,我們可以使用 perf inject 對 perf record 記錄的上下文切換的事件進(jìn)行處理,關(guān)聯(lián)出進(jìn)程從 CPU 切出 (swtich out) 和再次切入 (switch in) 的內(nèi)核代碼路徑,生成一個所謂的 Off CPU 火焰圖.
當(dāng)然,類似于鎖競爭這樣的比較簡單的問題,Off CPU 火焰圖足以一步定位出問題。但是對于更加復(fù)雜的因 D 狀態(tài)而阻塞的延遲問題,可能 Off CPU 火焰圖只能給我們一個調(diào)查的起點。
例如,當(dāng)我們看到,Off CPU 火焰圖的主要睡眠時間是因為 epoll_wait 等待引發(fā)的。那么,我們繼續(xù)要排查的應(yīng)該是網(wǎng)絡(luò)棧的延遲,即本文大圖中的 Net Delay 這部分。
至此,你也許會發(fā)現(xiàn),CPU iowait 和 idle 高的性能分析的實質(zhì)就是 延遲分析。這就是大圖按照內(nèi)核中資源管理的大方向,將延遲分析細(xì)化成了六大延遲分析:
CPU 延遲
內(nèi)存延遲
文件系統(tǒng)延遲
IO 棧延遲
網(wǎng)絡(luò)棧延遲
鎖及同步原語競爭
任何上述代碼路徑引發(fā)的 TASK_UNINTERRUPTIBLE 的睡眠,都是我們要分析的對象!
以問題結(jié)束
限于篇幅,本文很難將其所涉及的細(xì)節(jié)一一展開,因為讀到這里,你也許會發(fā)現(xiàn),原來 Load 高的分析,實際上就是對系統(tǒng)的全面負(fù)載分析。怪不得叫 System Load 呢。這也是 Load 分析為什么很難在一篇文章里去全面覆蓋。
本文也開啟了淺談 Linux 性能分析系列的第一章。后續(xù)我們會推出系列文章,就前文所述的六大延遲分析,一一展開介紹,敬請期待......
-
Linux
+關(guān)注
關(guān)注
87文章
11292瀏覽量
209326 -
Load
+關(guān)注
關(guān)注
0文章
9瀏覽量
10651
原文標(biāo)題:阿里楊勇:淺談 Linux 高負(fù)載的系統(tǒng)化分析
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論