深入理解load averages

本文翻譯自 linux-load-averages,按照譯者理解有刪減

前言

經(jīng)常和Linux打交道的童鞋都知道,load averages是衡量機器負載的關(guān)鍵指標,但是這個指標是怎樣定義出來的呢?

和其他系統(tǒng)不同,Linux上的load averages不僅追蹤可運行的任務(wù),還追蹤處于不可中斷睡眠狀態(tài)的任務(wù),為什么是這樣呢?這篇文章就來聊聊這方面的知識。

Linux的load averages是系統(tǒng)負載平均值,這個值將正在運行線程(任務(wù))對于系統(tǒng)的需求,作為處于運行和等待狀態(tài)的線程的平均數(shù)量。大多數(shù)工具會顯示1分鐘,5分鐘和15分鐘的平均值:

$ uptime
 17:30:01 up 13 days, 20:30,  3 users,  load average: 1.66, 2.03, 2.08
 
$ cat /proc/loadavg
1.48 1.98 2.06 4/3587 117385

對上面的輸出信息稍稍做些解釋

  • 如果平均值是0.0,說明系統(tǒng)處于空閑狀態(tài)
  • 如果1分鐘的平均值大于5分鐘或者15分鐘,說明系統(tǒng)負載正在增加
  • 如果1分鐘的平均值小于5分鐘或者15分鐘,說明系統(tǒng)負載正在減小
  • 如果這些值大于CPU的核數(shù),說明可能遇到了性能問題

利用這三個值,我們可以判斷系統(tǒng)的負載是在增加還是在減小,這在實踐中很有用。這三個中的任意一個拿出來也很有用,比如為云服務(wù)的自動伸縮設(shè)置閾值。不過,在缺少其他信息的情況下,單看這些值是沒有意義的。比如1分鐘的load averages值在23到25之間,就沒有任何意義;但如果知道CPU核數(shù)并且知道運行的任務(wù)是計算密集型,那這個值就很有意義。

歷史

最開始的時候,load averages只顯示對系統(tǒng)CPU相關(guān)的需求:運行的進程數(shù)加上等待的進程數(shù)。如RFC 546描述的

TENEX load averages是衡量CPU需求的指標。這個值是給定時間內(nèi)可運行進程數(shù)量的平均值。例如,對于單核CPU系統(tǒng),每小時平均10次意思是在該小時內(nèi)可以期望看到一個進程正在運行和另外九個等待CPU(即沒有被I/O阻塞)處于ready狀態(tài)的進程。

下圖是1973年繪制的監(jiān)控圖
image

以前操作系統(tǒng)的代碼還可以找到,下面是TENEX定義的一些宏

NRJAVS==3               ;NUMBER OF LOAD AVERAGES WE MAINTAIN
GS RJAV,NRJAVS          ;EXPONENTIAL AVERAGES OF NUMBER OF ACTIVE PROCESSES
[...]
;UPDATE RUNNABLE JOB AVERAGES

DORJAV: MOVEI 2,^D5000
        MOVEM 2,RJATIM          ;SET TIME OF NEXT UPDATE
        MOVE 4,RJTSUM           ;CURRENT INTEGRAL OF NBPROC+NGPROC
        SUBM 4,RJAVS1           ;DIFFERENCE FROM LAST UPDATE
        EXCH 4,RJAVS1
        FSC 4,233               ;FLOAT IT
        FDVR 4,[5000.0]         ;AVERAGE OVER LAST 5000 MS
[...]
;TABLE OF EXP(-T/C) FOR T = 5 SEC.

EXPFF:  EXP 0.920043902 ;C = 1 MIN
        EXP 0.983471344 ;C = 5 MIN
        EXP 0.994459811 ;C = 15 MIN

Linux中定義的宏長下面這樣(代碼出處include/linux/sched/loadavg.h):

#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) */

指標的三個粒度

load averages有1分鐘,5分鐘,15分鐘三個粒度的結(jié)果。不過事實上,他們并不是真正的平均值,統(tǒng)計的粒度也不是1,5,15分鐘。從上面的代碼中可以看出,1,5和15都是常量,用于計算指數(shù)衰減的5秒平均移動和。由此算出的1分鐘,5分鐘和15分鐘的load averages所反應(yīng)的負載遠遠超過1,5,15分鐘。

假設(shè)在一個空閑的系統(tǒng)上,開啟一個單線程來跑CPU密集任務(wù),60秒后的load averages是多少呢?如果load averages按普通平均值來算,這個值將是1.0。下面是一個繪制成圖的實驗結(jié)果

image

在上面的實驗中,所謂的“1分鐘load averages”在一分鐘內(nèi)只能達到0.62左右。

Linux不可中斷任務(wù)

Linux中剛引入load averages時,和其他系統(tǒng)一樣將其作為衡量CPU需求的指標,后來將其更改為不僅包含可運行任務(wù),還包含處于不可中斷狀態(tài)的任務(wù)(TASK_UNINTERRUPTIBLE或nr_uninterruptible)。這種狀態(tài)由希望避免信號中斷的代碼使用,其中包括阻塞在磁盤I/O和一些鎖上的任務(wù)。在pstop的輸出中,這種狀態(tài)被標志為“D”。ps(1)的man page將其稱為"不可中斷睡眠狀態(tài)(通常被IO阻塞)"

# man ps
.....
PROCESS STATE CODES
       Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the
       state of a process:

               D    uninterruptible sleep (usually IO)

...

為什么Linux中的load averages要加入不可中斷狀態(tài),而不是像其他系統(tǒng)一樣只計算CPU的需求呢?

加入不可中斷的起源

oldlinux.org找到了一封1993年的郵件

From: Matthias Urlichs <urlichs@smurf.sub.org>
Subject: Load average broken ?
Date: Fri, 29 Oct 1993 11:37:23 +0200


The kernel only counts "runnable" processes when computing the load average.
I don't like that; the problem is that processes which are swapping or
waiting on "fast", i.e. noninterruptible, I/O, also consume resources.

It seems somewhat nonintuitive that the load average goes down when you
replace your fast swap disk with a slow swap disk...

Anyway, the following patch seems to make the load average much more
consistent WRT the subjective speed of the system. And, most important, the
load is still zero when nobody is doing anything. ;-)

--- kernel/sched.c.orig Fri Oct 29 10:31:11 1993
+++ kernel/sched.c  Fri Oct 29 10:32:51 1993
@@ -414,7 +414,9 @@
    unsigned long nr = 0;

    for(p = &LAST_TASK; p > &FIRST_TASK; --p)
-       if (*p && (*p)->state == TASK_RUNNING)
+       if (*p && ((*p)->state == TASK_RUNNING) ||
+                  (*p)->state == TASK_UNINTERRUPTIBLE) ||
+                  (*p)->state == TASK_SWAPPING))
            nr += FIXED_1;
    return nr;
 }
--
Matthias Urlichs        \ XLink-POP N|rnberg   | EMail: urlichs@smurf.sub.org
Schleiermacherstra_e 12  \  Unix+Linux+Mac     | Phone: ...please use email.
90491 N|rnberg (Germany)  \   Consulting+Networking+Programming+etc'ing      42

看到這么久之前的想法還是很令人驚嘆的。

這也證明了Linux改變load averages的含義,使其不僅體現(xiàn)對CPU的需要,是有意的,這讓load averages從“CPU負載均衡”變成了“系統(tǒng)負載均衡”。

郵件中舉交換磁盤速度慢的例子是有道理的:通過降低系統(tǒng)性能,系統(tǒng)需求(運行和排隊的進程數(shù))應(yīng)該增加;但是如果僅僅根據(jù)CPU運行狀態(tài),那么load averages值應(yīng)該會下降。Matthias認為這是不直觀的,所以修改了代碼。

現(xiàn)代系統(tǒng)的不可中斷

但是難道不會出現(xiàn)磁盤I/O不能解釋Linux load averages過高的情況嗎?這種情況是會出現(xiàn)的,這是因為在現(xiàn)代Linux(4.12)版本中,有將近400處代碼設(shè)置了TASK_UNINTERRUPTIBLE狀態(tài),包括一些鎖原語中。其中部分代碼可能不需要統(tǒng)計在load averages中。

既然TASK_UNINTERRUPTIBLE在更多的地方被用到,那么是否應(yīng)該將load averages改成只統(tǒng)計CPU和磁盤需求呢?Linux調(diào)度程序的維護者Peter Zijstra有一個想法:將TASK_UNINTERRUPTIBLE替換成task_struct->in_iowait,這樣load averages就更貼近磁盤I/O的需求。這樣又引入了另外一個問題,我們到底想要從load averages中得到什么?我們是需要用線程對系統(tǒng)的需求來衡量負載,還是只通過物理資源的使用情況來衡量負載呢?如果是前者的話,那么應(yīng)該包含等待不間斷鎖的線程,因為這些線程并沒有閑置。所以也許Linux的load averages已經(jīng)按我們需要的方式工作了。

理解Linux的load averages

也許真正的問題在于“l(fā)oad averages”這個詞和“I/O”一樣含糊不清。到底是哪種I/O呢?是磁盤I/O?文件系統(tǒng)I/O?還是網(wǎng)絡(luò)I/O。類似的,到底是哪種load averages呢?是CPU平均負載?還是系統(tǒng)平均負載?下面做一個總結(jié)吧:

  • 在Linux上,load averages的真實含義是“系統(tǒng)平均負載”,即對整個系統(tǒng),測量正在工作并等待工作的線程數(shù)(CPU,磁盤,不可中斷鎖)。換句話說,這種方式測量的是不完全空閑的線程數(shù)量。這種方式的優(yōu)勢在于包括了對不同資源的需求
  • 在其他的系統(tǒng)上,load averages的含義是“CPU平均負載”,這組值用于測量正在占有CPU執(zhí)行權(quán)的線程數(shù)量加上等待CPU的線程數(shù)量。

還有另一種可能的類型:“物理資源負載平均值”,其中包括僅用于物理資源(CPU+磁盤)的負載。

更精確的測量數(shù)據(jù)

當Linux的load averages值增加時,可以判斷任務(wù)對系統(tǒng)資源(CPU,磁盤和鎖)有了更高的需求,但是到底是對哪種資源的需求增長了呢?這時可以用其他的指標來進行判斷。比如,CPU資源有如下指標:

  • 單個CPU使用率:可以用命令mpstat -P ALL 1查看
  • 每個進程的CPU使用率:可用命令top,pidstat 1查看
  • 每個線程運行隊列(調(diào)度程序)延遲:可用命令perf sched查看,也可以查看文件/proc/PID/schedstats
  • CPU運行隊列延遲:可用命令perf sched查看,也可以查看文件/proc/schedstat
  • CPU運行隊列長度:可用vmstat 1命令查看。

上面提供的指標中,前兩個用來衡量使用率,后三個用來度量系統(tǒng)飽和度。利用率指標對于衡量工作負載很有用,而飽和度指標可用來識別性能問題。衡量CPU飽和度的最佳指標是運行隊列(或調(diào)度程序)的延遲,延遲是指任務(wù)或者線程處于可運行狀態(tài),但必須等待CPU的時間。通過這樣的指標可以用來衡量性能問題的嚴重程度,比如線程等待調(diào)度的時間在運行時間中占的百分比。通過觀察運行隊列長度可以很方便判斷是否存在問題,但比較難定位到問題產(chǎn)生的原因。

schedstats功能在Linux 4.6中成為內(nèi)核可調(diào)參數(shù)(sysctl.kernel.sched_schedstats),默認是關(guān)閉的。

盡管有更明確的指標,但并不意味著load averages是無用的。這組指標已經(jīng)成功用于云計算微服務(wù)的擴展策略,微服務(wù)根據(jù)不同的負載值做出反應(yīng)。有了這些判斷的依據(jù),即使在自動擴容時犯錯也保險多了:擴容實例會花更多的錢,不擴容則會損失用戶。如果擴容太多,后來調(diào)查一下糾正就是了。

總結(jié)

在1993年,一位Linux工程師發(fā)現(xiàn)了一個非直觀的load averages情況,于是提交了三行代碼的補丁將load averages的含義由“CPU負載平均值”變成了“系統(tǒng)負載平均值”。這次的變動在統(tǒng)計中包括了不可中斷狀態(tài)下的任務(wù),所以load averages值不僅反映了對CPU的需求,還反映了對磁盤資源的需求。系統(tǒng)平均負載計算正在工作和等待工作的線程的數(shù)量,并且統(tǒng)計1分鐘,5分鐘,15分鐘指數(shù)衰減的移動總和平均值。通過這三個值,能夠知道系統(tǒng)的負載是在增加還是在減小。

Linux中對不可中斷狀態(tài)的使用越來越多,現(xiàn)在已經(jīng)包括了不可中斷的鎖原語。如果需要衡量處于運行狀態(tài)和等待狀態(tài)的線程對于系統(tǒng)的需求,那么load averages依然是很好的指標。

最后引用kernel/sched/loadavg.c頭部的注釋來結(jié)束吧

  • This file contains the magic bits required to compute the global loadavg
  • figure. Its a silly number but people think its important. We go through
  • great pains to make it work on big machines and tickless kernels.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容