原文:https://mp.weixin.qq.com/s?src=11×tamp=1533697106&ver=1047&signature=poqrJFfcNABv4biKKpa4mZdIW7No2Wo1F5sbZL7ggoVS2GqcSqwQQ8hMulAmezT*zL*klB-eE5BeMyNuyjuIH7YgkBAN25i6*ahhEpWyxqx6vPct-Vr7q7AU0YGe-F*l&new=1
http://blog.scoutapp.com/articles/2009/07/31/understanding-load-averages
經常和 Linux 打交道的童鞋都知道,load averages 是衡量機器負載的關鍵指標,但是這個指標是怎樣定義出來的呢?
和其他系統不同,Linux 上的 load averages 不僅追蹤可運行的任務,還追蹤處于不可中斷睡眠狀態的任務,為什么是這樣呢?這篇文章就來聊聊這方面的知識。
Linux 的 load averages 是系統負載平均值,這個值將正在運行線程(任務)對于系統的需求,作為處于運行和等待狀態的線程的平均數量。大多數工具會顯示 1 分鐘,5 分鐘和 15 分鐘的平均值:
$ uptime17: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,說明系統處于空閑狀態
- 如果 1 分鐘的平均值大于 5 分鐘或者 15 分鐘,說明系統負載正在增加
- 如果 1 分鐘的平均值小于 5 分鐘或者 15 分鐘,說明系統負載正在減小
- 如果這些值大于 CPU 的核數,說明可能遇到了性能問題
利用這三個值,我們可以判斷系統的負載是在增加還是在減小,這在實踐中很有用。這三個中的任意一個拿出來也很有用,比如為云服務的自動伸縮設置閾值。不過,在缺少其他信息的情況下,單看這些值是沒有意義的。比如 1 分鐘的 load averages 值在 23 到 25 之間,就沒有任何意義;但如果知道 CPU 核數并且知道運行的任務是計算密集型,那這個值就很有意義。
歷史
最開始的時候,load averages 只顯示對系統 CPU 相關的需求:運行的進程數加上等待的進程數。如 RFC 546 描述的:
TENEX load averages 是衡量 CPU 需求的指標。這個值是給定時間內可運行進程數量的平均值。例如,對于單核 CPU 系統,每小時平均 10 次意思是在該小時內可以期望看到一個進程正在運行和另外九個等待 CPU(即沒有被 I/O 阻塞) 處于 ready 狀態的進程。
下圖是 1973 年繪制的監控圖:
以前操作系統的代碼還可以找到,下面是 TENEX) 定義的一些宏:
NRJAVS==3 ;NUMBER OF LOAD AVERAGES WE MAINTAIN GS RJAV,NRJAVS ;EXPONENTIAL AVERAGES OF NUMBER OF ACTIVE PROCESSES [...] ;UPDATE RUNNABLE JOB AVERAGESDORJAV: MOVEI 2,^D5000MOVEM 2,RJATIM ;SET TIME OF NEXT UPDATEMOVE 4,RJTSUM ;CURRENT INTEGRAL OF NBPROC+NGPROCSUBM 4,RJAVS1 ;DIFFERENCE FROM LAST UPDATEEXCH 4,RJAVS1FSC 4,233 ;FLOAT ITFDVR 4,[5000.0] ;AVERAGE OVER LAST 5000 MS [...] ;TABLE OF EXP(-T/C) FOR T = 5 SEC.EXPFF: EXP 0.920043902 ;C = 1 MINEXP 0.983471344 ;C = 5 MINEXP 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 分鐘三個粒度的結果。不過事實上,他們并不是真正的平均值,統計的粒度也不是 1,5,15 分鐘。從上面的代碼中可以看出,1,5 和 15 都是常量,用于計算指數衰減的 5 秒平均移動和。由此算出的 1 分鐘,5 分鐘和 15 分鐘的 load averages 所反應的負載遠遠超過 1,5,15 分鐘。
假設在一個空閑的系統上,開啟一個單線程來跑 CPU 密集任務,60 秒后 1 分鐘的 load averages 是多少呢?如果 load averages 按普通平均值來算,這個值將是 1.0. 下面是一個繪制成圖的實驗結果:
在上面的實驗中,所謂的“1 分鐘 load averages”在一分鐘內只能達到 0.62 左右。
Linux 不可中斷任務
Linux 中剛引入 load averages 時,和其他系統一樣將其作為衡量 CPU 需求的指標,后來將其更改為不僅包含可運行任務,還包含處于不可中斷狀態的任務(TASK_UNINTERRUPTIBLE 或 nr_uninterruptible)。這種狀態由希望避免信號中斷的代碼使用,其中包括阻塞在磁盤 I/O 和一些鎖上的任務。在ps
和top
的輸出中,這種狀態被標志為“D”。ps(1) 的 man page 將其稱為"不可中斷睡眠狀態(通常被 IO 阻塞)"
# man ps ..... PROCESS STATE CODESHere are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe thestate of a process:D uninterruptible sleep (usually IO)...
為什么 Linux 中的 load averages 要加入不可中斷狀態呢,而不是像其他系統一樣只計算 CPU 的需求呢?
加入不可中斷的起源
在 oldlinux.org 找到了一封 1993 年的郵件:
From: Matthias Urlichs <urlichs@smurf.sub.org> Subject: Load average broken ? Date: Fri, 29 Oct 1993 11:37:23 +0200The 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 的含義,使其不僅體現對 CPU 的需要,是有意的,這讓 load averages 從“CPU 負載均衡”變成了“系統負載均衡”。
郵件中舉交換磁盤速度慢的例子是有道理的:通過降低系統性能,系統需求(運行和排隊的進程數)應該增加;但是如果僅僅根據 CPU 運行狀態,那么 load averages 值應該會下降。Matthias 認為這是不直觀的,所以修改了代碼。
現代系統的不可中斷
但是難道不會出現磁盤 I/O 不能解釋 Linux load averages 過高的情況嗎?這種情況是會出現的,這是因為在現代 Linux(4.12)版本中,有將近 400 處代碼設置了TASK_UNINTERRUPTIBLE
狀態,包括一些鎖原語中。其中部分代碼可能不需要統計在 load averages 中。
既然TASK_UNINTERRUPTIBLE
在更多的地方被用到,那么是否應該將 load averages 改成只統計 CPU 和磁盤需求呢?Linux 調度程序的維護者 Peter Zijstra 有一個想法:將TASK_UNINTERRUPTIBLE
替換成task_struct->in_iowait
,這樣 load averages 就更貼近磁盤 I/O 的需求。這樣又引入了另外一個問題,我們到底想要從 load averages 中得到什么?我們是需要用線程對系統的需求來衡量負載,還是只通過物理資源的使用情況來衡量負載呢?如果是前者的話,那么應該包含等待不間斷鎖的線程,因為這些線程并沒有閑置。所以也許 Linux 的 load averages 已經按我們需要的方式工作了。
理解 Linux 的 load averages
也許真正的問題在于“load averages”這個詞和“I/O”一樣含糊不清。到底是哪種 I/O 呢?是磁盤 I/O?文件系統 I/O?還是網絡 I/O。類似的,到底是哪種 load averages 呢?是 CPU 平均負載?還是系統平均負載?下面做一個總結吧:
- 在 Linux 上,load averages 的真實含義是“系統平均負載”,即對整個系統,測量正在工作并等待工作的線程數(CPU,磁盤,不可中斷鎖)。換句話說,這種方式測量的是不完全空閑的線程數量。這種方式的優勢在于包括了對不同資源的需求。
- 在其他的系統上,load averages 的含義是“CPU 平均負載”,這組值用于測量正在占有 CPU 執行權的線程數量加上等待 CPU 的線程數量。
還有另一種可能的類型:“物理資源負載平均值”,其中包括僅用于物理資源(CPU+ 磁盤)的負載。
更精確的測量數據
當 Linux 的 load averages 值增加時,可以判斷任務對系統資源(CPU,磁盤和鎖)有了更高的需求,但是到底是對哪種資源的需求增長了呢?這時可以用其他的指標來進行判斷。比如,CPU 資源有如下指標:
- 單個 CPU 使用率:可以用命令
mpstat -P ALL 1
查看 - 每個進程的 CPU 使用率:可用命令
top
,pidstat 1
查看 - 每個線程運行隊列(調度程序)延遲:可用命令
perf sched
查看,也可以查看文件/proc/PID/schedstats
- CPU 運行隊列延遲:可用命令
perf sched
查看,也可以查看文件/proc/schedstat
- CPU 運行隊列長度:可用
vmstat 1
命令查看。
上面提供的指標中,前兩個用來衡量使用率,后三個用來度量系統飽和度。利用率指標對于衡量工作負載很有用,而飽和度指標可用來識別性能問題。衡量 CPU 飽和度的最佳指標是運行隊列(或調度程序)的延遲,延遲是指任務或者線程處于可運行狀態,但必須等待 CPU 的時間。通過這樣的指標可以用來衡量性能問題的嚴重程度,比如線程等待調度的時間在運行時間中占的百分比。通過觀察運行隊列長度可以很方便判斷是否存在問題,但比較難定位到問題產生的原因。
schedstats
功能在 Linux 4.6 中成為內核可調參數(sysctl.kernel.sched_schedstats),默認是關閉的。
盡管有更明確的指標,但并不意味著 load averages 是無用的。這組指標已經成功用于云計算微服務的擴展策略,微服務根據不同的負載值做出反應。有了這些判斷的依據,即使在自動擴容時犯錯也保險多了:擴容實例會花更多的錢,不擴容則會損失用戶。如果擴容太多,后來調查一下糾正就是了。
總結
在 1993 年,一位 Linux 工程師發現了一個非直觀的 load averages 情況,于是提交了三行代碼的補丁將 load averages 的含義由“CPU 負載平均值”變成了“系統負載平均值”。這次的變動在統計中包括了不可中斷狀態下的任務,所以 load averages 值不僅反映了對 CPU 的需求,還反映了對磁盤資源的需求。系統平均負載計算正在工作和等待工作的線程的數量,并且統計 1 分鐘,5 分鐘,15 分鐘指數衰減的移動總和平均值。通過這三個值,能夠知道系統的負載是在增加還是在減小。
Linux 中對不可中斷狀態的使用越來越多,現在已經包括了不可中斷的鎖原語。如果需要衡量處于運行狀態和等待狀態的線程對于系統的需求,那么 load averages 依然是很好的指標。
最后引用 kernel/sched/loadavg.c 頭部的注釋來結束吧。
- 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.