作者簡介:呂夏飛,西安郵電大學2018級陳莉君教授研究生,技術宅一枚,喜歡折騰各種新技術,江湖人稱C小子。
01
“平均負載”是什麼?
當系統變慢時,我們做的第一件事就是在Linux系統終端中輸入uptime、w、top、glances、tload等命令來瞭解系統的負載情況,這幾個命令都會有系統平均負載load average的輸出,那麼系統平均負載是什麼呢?
執行佇列中的平均行程數
Load Average是 CPU 的Load,它所包含的資訊不是 CPU 的使用率狀況,而是在一段時間內 CPU 正在處理以及等待 CPU 處理的行程數之和的統計資訊,也就是 CPU 使用佇列的長度的統計資訊。
也就是說:
系統平均負載被定義為在特定時間間隔內執行佇列中的平均行程數。
如果一個滿足以下條件則其就會位於執行佇列中:
-
它沒有在等待I/O操作的結果
-
它沒有主動進入等待狀態(也就是沒有呼叫’wait’)
-
沒有被停止(例如:等待終止)
平均活躍行程數
簡單來說,平均負載是指單位時間內,系統處於可執行狀態和不可中斷狀態的平均行程數,也就是平均活躍行程數,它和 CPU 利用率並沒有直接關係。這裡先解釋下,可執行狀態和不可中斷狀態這倆詞兒。
可執行狀態
所謂可執行狀態的行程,是指正在使用 CPU 或者正在等待 CPU 的行程,也就是我們常用 ps 命令看到的,處於 R 狀態(Running 或 Runnable)的行程。
不可中斷狀態
不可中斷狀態的行程則是正處於核心態關鍵流程中的行程,並且這些流程是不可打斷的,比如最常見的是等待硬體裝置的 I/O 響應,也就是我們在 ps 命令中看到的 D 狀態(Uninterruptible Sleep,也稱為 Disk Sleep)的行程。
比如,當一個行程向磁碟讀寫資料時,為了保證資料的一致性,在得到磁碟迴復前,它是不能被其他行程或者中斷打斷的,這個時候的行程就處於不可中斷狀態。如果此時的行程被打斷了,就容易出現磁碟資料與行程資料不一致的問題。
所以,不可中斷狀態實際上是系統對行程和硬體裝置的一種保護機制。
因此,你可以簡單理解為,平均負載其實就是平均活躍行程數。平均活躍行程數,直觀上的理解就是單位時間內的活躍行程數,但它實際上是活躍行程數的指數衰減平均值。這個“指數衰減平均”的詳細含義你不用計較,這隻是系統的一種更快速的計算方式,你把它直接當成活躍行程數的平均值也沒問題。
02
檢視系統平均負載
uptime
ubuntu@localhost:~$ uptime
21:41:11 up 57 min, 1 user, load average: 0.28, 0.09, 0.24
命令輸出的最後內容表示在過去的1、5、15分鐘內執行佇列中的平均行程數量。一般來說只要每個 CPU 的當前活動行程數不大於3那麼系統的效能就是良好的,如果每個 CPU 的任務數大於5,那麼就表示這臺機器的效能有嚴重問題。透過
ubuntu@localhost:~$ grep 'model name' /proc/cpuinfo | wc -l
1
可知該系統此時只有一個 CPU ,則表示其系統效能是良好的。
w
top
glances
tload
loadavg
這些工具中的平均負載是從 /proc/loadavg 檔案中讀取的,也可以直接使用 cat 命令檢視:
ubuntu@localhost:~$ cat /proc/loadavg
0.48 0.69 0.42 5/452 6570
03
理解系統平均負載和CPU核心數目的關係
考慮了 CPU 核心數的影響,才能解釋系統負載。
多處理器 Vs 多核處理器
多處理器 —— 一個計算機系統中整合兩個或多個物理 CPU
多核處理器 —— 單個物理 CPU 有兩個或多個單獨的核並行工作(也叫處理單元)。雙核意味著有兩個處理單元,4核有4個處理單元,以此類推。
此外,Intel 引入了超執行緒技術用來提高平行計算能力。
透過超執行緒技術,在作業系統中,單個物理 CPU 表現的和兩個邏輯 CPU 一樣。(實際在硬體上只有一個 CPU)。
註意,單個 CPU 核同一時間只能執行一個任務,於是產生了多 CPU/處理器、多核 CPU,以及多執行緒技術。
多 CPU 時,多個程式可以同時執行。如今的 Intel CPU 使用了多核心和超執行緒技術。
可以使用 nproc 或 lscpu 命令檢視系統中的處理器單元數量。
ubuntu@localhost:~$ nproc
4
# 或者
lscpu
也可以使用 grep 命令:
ubuntu@localhost:~$ grep 'model name' /proc/cpuinfo | wc -l
4
為了進一步理解系統負載,需要做一些假設。假設系統負載如下:
23:16:49 up 10:49, 5 user, load average: 1.00, 0.40, 3.35
在單核系統中意味著:
-
CPU 被充分利用(100%);最近的 1 分鐘有 1 個行程在執行。
-
CPU 有 60% 處於空閑狀態;在最近的 5 分鐘沒有行程等待 CPU 時間。
-
CPU 平均過載了 235%;最近的 15 分鐘平均有 2.35 個行程在等待 CPU 時間。
在雙核系統中意味著:
-
有一個 CPU 處於完全空閑狀態,另一個 CPU 被使用;最近的 1 分鐘沒有行程等待 CPU 時間。
-
CPU 平均 160% 處於空閑狀態;最近的 5 分鐘沒有行程等待 CPU 時間。
-
CPU 平均過載了 135%;最近的 15 分鐘有 1.35 個行程等待 CPU 時間。
04
Load average 的演演算法
上面的輸出資料是每隔5秒鐘檢查一次活躍的行程數,然後根據這個數值算出來的。如果這個數除以 CPU 的數目,結果高於5的時候就表明系統在超負荷運轉了。其演演算法(摘自Linux 2.6 的核心程式碼)如下:
檔案: include/linux/sched.h:
#define FSHIFT 11 /* nr of bits of precision */
#define FIXED_1 (1</* 1.0 as fixed-point */
#define LOAD_FREQ (5*HZ) /* 5 sec intervals */
#define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
#define EXP_5 2014 /* 1/exp(5sec/5min) */
#define EXP_15 2037 /* 1/exp(5sec/15min) */
#define CALC_LOAD(load,exp,n) \
load *= exp; \
load += n*(FIXED_1-exp); \
load >>= FSHIFT;
檔案: kernel/timer.c:
unsigned long avenrun[3];
/*
* calc_load - given tick count, update the avenrun load estimates.
* This is called while holding a write_lock on xtime_lock.
*/
static inline void calc_load(unsigned long ticks)
{
unsigned long active_tasks; /* fixed-point */
static int count = LOAD_FREQ;
count -= ticks;
if (count 0) {
count += LOAD_FREQ;
active_tasks = count_active_tasks();
CALC_LOAD(avenrun[0], EXP_1, active_tasks);
CALC_LOAD(avenrun[1], EXP_5, active_tasks);
CALC_LOAD(avenrun[2], EXP_15, active_tasks);
}
}
檔案: fs/proc/proc_misc.c:
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
/*
* Warning: stuff below (imported functions) assumes that its output will fit
* into one page. For some of those functions it may be wrong. Moreover, we
* have a way to deal with that gracefully. Right now I used straightforward
* wrappers, but this needs further analysis wrt potential overflows.
*/
extern int get_hardware_list(char *);
extern int get_stram_list(char *);
extern int get_chrdev_list(char *);
extern int get_filesystem_list(char *);
extern int get_exec_domain_list(char *);
extern int get_dma_list(char *);
extern int get_locks_status (char *, char **, off_t, int);
static int proc_calc_metrics(char *page, char **start, off_t off,
int count, int *eof, int len)
{
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static int loadavg_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int a, b, c;
int len;
a = avenrun[0] + (FIXED_1/200);
b = avenrun[1] + (FIXED_1/200);
c = avenrun[2] + (FIXED_1/200);
len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%d %d\n",
LOAD_INT(a), LOAD_FRAC(a),
LOAD_INT(b), LOAD_FRAC(b),
LOAD_INT(c), LOAD_FRAC(c),
nr_running(), nr_threads, last_pid);
return proc_calc_metrics(page, start, off, count, eof, len);
}
05
平均負載的意義
既然平均的是活躍行程數,那麼最理想的,就是每個 CPU 上都剛好執行著一個行程,這樣每個 CPU 都得到了充分利用。比如當平均負載為 2 時,意味著什麼呢?
-
在只有 2 個 CPU 的系統上,意味著所有的 CPU 都剛好被完全佔用。
-
在 4 個 CPU 的系統上,意味著 CPU 有 50% 的空閑。
-
而在只有 1 個 CPU 的系統中,則意味著有一半的行程競爭不到 CPU。
06
平均負載為多少時合理?
回到前面的例子,不知道能否判斷出,在那些命令的結果裡,其中三個時間段的平均負載數,多大的時候能說明系統負載高?或是多小的時候就能說明系統負載很低呢?
我們知道,平均負載最理想的情況是等於 CPU 個數。所以在評判平均負載時,首先需要知道系統有幾個 CPU,這可以透過 top 命令或者從檔案 /proc/cpuinfo 中讀取。有了 CPU 個數,我們就可以判斷出,當平均負載比 CPU 個數還大的時候,系統已經出現了過載。
不過,新的問題又來了。在前面的例子中可以看到,平均負載有三個數值,到底該參考哪一個呢?
實際上,都要看。三個不同時間間隔的平均值,其實給我們提供了,分析系統負載趨勢的資料來源,可以更全面、更立體地理解目前的負載狀況。
平均負載的三個時間段
打個比方,就像初秋時北京的天氣,如果只看中午的溫度,你可能以為還在 7 月份的大夏天呢。但如果你結合了早上、中午、晚上三個時間點的溫度來看,基本就可以全方位瞭解這一天的天氣情況了。
同樣的,前面說到的 CPU 的三個負載時間段也是這個道理。
-
如果 1 分鐘、5 分鐘、15 分鐘的三個值基本相同,或者相差不大,那就說明系統負載很平穩。
-
但如果 1 分鐘的值遠小於 15 分鐘的值,就說明系統最近 1 分鐘的負載在減少,而過去 15 分鐘內卻有很大的負載。
-
反過來,如果 1 分鐘的值遠大於 15 分鐘的值,就說明最近 1 分鐘的負載在增加,這種增加有可能只是臨時性的,也有可能還會持續增加下去,所以就需要持續觀察。一旦 1 分鐘的平均負載接近或超過了 CPU 的個數,就意味著系統正在發生過載的問題,這時就得分析調查是哪裡導致的問題,並要想辦法優化了。
這裡舉個例子,假設在一個單 CPU 系統上看到平均負載為 1.73,0.60,7.98,那麼說明在過去 1 分鐘內,系統有 73% 的超載,而在 15 分鐘內,有 698% 的超載,從整體趨勢來看,系統的負載在降低。
實際環境中的平均負載
在實際生產環境中,平均負載多高時,需要我們重點關註呢?
一般情況,當平均負載高於 CPU 數量 70% 的時候,就應該分析排查負載高的問題了。一旦負載過高,就可能導致行程響應變慢,進而影響服務的正常功能。
但 70% 這個數字並不是絕對的,最推薦的方法,還是把系統的平均負載監控起來,然後根據更多的歷史資料,判斷負載的變化趨勢。當發現負載有明顯升高趨勢時,比如說負載翻倍了,再去做分析和調查。
參考資料:
-
Linux效能最佳化實戰 倪朋飛(https://time.geekbang.org/column/140)
-
什麼是系統平均負載(Load average) (https://www.cnblogs.com/pangguoping/p/5589027.html)
-
理解 Linux 的平均負載和效能監控 (https://linux.cn/article-8632-1.html)
—— E N D ——