歡迎光臨
每天分享高質量文章

什麼是“平均負載”(一)?

作者簡介:呂夏飛,西安郵電大學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

[email protected]:~$ uptime
21:41:11 up 57 min,  1 user,  load average: 0.280.09, 0.24

命令輸出的最後內容表示在過去的1、5、15分鐘內運行佇列中的平均行程數量。一般來說只要每個 CPU 的當前活動行程數不大於3那麼系統的性能就是良好的,如果每個 CPU 的任務數大於5,那麼就表示這台機器的性能有嚴重問題。通過

[email protected]:~$ grep 'model name' /proc/cpuinfo | wc -l
1 

可知該系統此時只有一個 CPU ,則表示其系統性能是良好的。

w

top

glances

tload

loadavg

這些工具中的平均負載是從 /proc/loadavg 檔案中讀取的,也可以直接使用 cat 命令查看:

[email protected]:~$ 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 命令查看系統中的處理器單元數量。

[email protected]:~$ nproc
4
# 或者
lscpu
也可以使用 grep 命令:
[email protected]:~$ 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_tint);

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 ——

赞(0)

分享創造快樂