Linux 平均负载:解开谜团

平均负载是一个行业关键指标——我的公司根据它们和其他指标花费数百万自动扩展云实例——但在 Linux 上,它们周围有一些谜团。Linux 平均负载不仅跟踪可运行的任务,还跟踪处于不间断睡眠状态的任务。为什么?我从未见过解释。在这篇文章中,我将解开这个谜团,并总结负载平均值作为每个试图解释它们的参考。
Linux 负载平均值是“系统负载平均值”,它将系统上正在运行的线程(任务)需求显示为正在运行的线程加上等待的线程的平均数量。这可以衡量需求,这可能大于系统当前正在处理的需求。大多数工具显示三个平均值,分别为 1、5 和 15 分钟:
$ 正常运行时间
16:48:24 向上 4:11,1 个用户,平均负载:25.25、23.40、23.46

top - 16:48:42 up 4:12, 1 个用户,平均负载:25.25, 23.14, 23.37

$ cat /proc/loadavg
25.72 23.19 23.35 42/3411 43603
一些解释:
如果平均值为 0.0,则您的系统处于空闲状态。
如果 1 分钟平均值高于 5 或 15 分钟平均值,则负载正在增加。
如果 1 分钟平均值低于 5 或 15 分钟平均值,则负载正在减少。
如果它们高于您的 CPU 计数,那么您可能会遇到性能问题(这取决于)。
作为一组三个,您可以判断负载是增加还是减少,这很有用。当需要单个需求值时,它们也很有用,例如云自动扩展规则。但是如果没有其他指标的帮助,很难更详细地理解它们。23 - 25 的单个值本身并没有任何意义,但如果 CPU 计数已知并且已知它是受 CPU 限制的工作负载,则可能意味着什么。
我通常不会尝试调试负载平均值,而是切换到其他指标。我将在接近结尾的“更好的指标”部分讨论这些。
历史
原始负载平均值仅显示 CPU 需求:正在运行的进程数加上等待运行的进程数。在 1973 年 8 月的RFC 546标题为“TENEX Load Averages”中有一个很好的描述:
TENEX 平均负载是衡量 CPU 需求的指标。 平均负载是给定时间段内可运行进程数的平均值。 例如,每小时平均负载为 10 意味着(对于单个 CPU 系统)在该小时内的任何时间,人们都可以预期看到 1 个进程正在运行,而其他 9 个进程准备好运行(即,未因 I/O 阻塞)等待 对于 CPU
ietf.org 上的这个版本链接到 1973 年 7 月的手绘负载平均图的 PDF 扫描,显示这已经被监控了几十年:

来源:https://tools.ietf.org/html/rfc546
如今,旧操作系统的源代码也可以在网上找到。这是来自 TENEX(1970 年代初期)SCHED.MAC 的 DEC 宏程序集的一个例外:
NRJAVS==3 ;我们维持的平均负载数量
GS RJAV,NRJAVS ;活动进程数的指数平均值
[…]
;更新可运行的作业平均值

多尔哈夫:MOVEI 2,^D5000
MOVEM 2,RJATIM ;设置下次更新时间
MOVE 4,RJTSUM ;NBPROC+NGPROC 的当前积分
SUBM 4,RJAVS1 ;与上次更新的区别
EXCH 4,JAVS1
FSC 4,233 ;浮动它
FDVR 4,[5000.0] ;过去 5000 毫秒的平均值
[…]
; T = 5 秒的 EXP(-T/C) 表。

EXPFF:EXP 0.920043902 ;C = 1 分钟
经验 0.983471344 ;C = 5 分钟
经验 0.994459811 ;C = 15 分钟
这是今天 Linux 的摘录(include/linux/sched/loadavg.h):
#define EXP_1 1884 /* 1/exp(5sec/1min) 为定点 /
#define EXP_5 2014 /
1/exp(5sec/5min) /
#define EXP_15 2037 /
1/exp(5sec/15min) */
Linux 还对 1、5 和 15 分钟常量进行了硬编码。
在旧系统中也有类似的平均负载指标,包括 Multics,它具有指数调度队列平均值。
三个数字
这三个数字是 1、5 和 15 分钟的平均负载。除了它们不是真正的平均值,它们也不是 1、5 和 15 分钟。从上面的源代码中可以看出,1、5 和 15 分钟是方程中使用的常数,用于计算 5 秒平均值的指数阻尼移动和。生成的 1、5 和 15 分钟负载平均值反映了远远超过 1、5 和 15 分钟的负载。
如果您使用空闲系统,然后开始单线程 CPU 密集型工作负载(循环中的一个线程),60 秒后一分钟的平均负载是多少?如果它是一个普通的平均值,它将是 1.0。这是那个实验,图表:

负载平均实验以可视化指数阻尼
所谓的“一分钟平均值”,到一分钟大关才达到0.62左右。有关方程和类似实验的更多信息,Neil Gunther 博士写了一篇关于平均负载的文章:它是如何工作的,此外 loadavg.c 中有许多 Linux 源代码块注释。
Linux 不间断任务
当负载平均值首次出现在 Linux 中时,它们反映了 CPU 需求,与其他操作系统一样。但后来在 Linux 上将它们更改为不仅包括可运行任务,还包括处于不可中断状态的任务(TASK_UNINTERRUPTIBLE 或 nr_uninterruptible)。此状态由希望避免信号中断的代码路径使用,其中包括在磁盘 I/O 上阻塞的任务和一些锁。您之前可能已经看到过这种状态:它在输出ps和top中显示为“D”状态。ps(1) 手册页称其为“不间断睡眠(通常是 IO)”。
添加不间断状态意味着 Linux 平均负载会因磁盘(或 NFS)I/O 工作负载而增加,而不仅仅是 CPU 需求。对于熟悉其他操作系统及其 CPU 平均负载的每个人来说,包括这种状态,起初都令人深感困惑。
为什么?确切地说,为什么 Linux 会这样做?
有无数关于平均负载的文章,其中许多都指出了 Linux nr_uninterruptible 陷阱。但是我没有看到任何解释甚至冒险猜测它为什么被包括在内的东西。我自己的猜测是,它旨在反映更一般意义上的需求,而不仅仅是 CPU 需求。
寻找一个古老的 Linux 补丁
理解为什么在 Linux 中发生了更改很容易:您阅读相关文件的 git 提交历史记录并阅读更改描述。我检查了loadavg.c上的历史记录,但添加不间断状态的更改早于该文件,该文件是使用早期文件中的代码创建的。我检查了另一个文件,但这条线索也很冷:代码本身已经在不同的文件中跳跃。为了走捷径,我为整个 Linux github 存储库转储了“git log -p”,这是 4 GB 的文本,然后开始向后阅读,看看代码第一次出现的时间。这也是一个死胡同。整个 Linux 存储库中最古老的变化可以追溯到 2005 年,当时 Linus 导入了 Linux 2.6.12-rc2,而这个变化早于那个。
有历史的 Linux 存储库(此处和此处),但这些更改描述也缺失。试图发现,至少,当这个变化发生时,我在kernel.org上搜索了 tarball ,发现它已经改变了 0.99.15,而不是 0.99.13——但是,0.99.14 的 tarball 丢失了。我在其他地方找到了它,并确认更改是在 1993 年 11 月的 Linux 0.99 补丁级别 14 中。我希望 Linus 对 0.99.14 的发布描述能够解释该更改,但这也是一个死胡同:
“对上一个正式版本 (p13) 的更改太多了,无法提及(甚至无法记住)…” – Linus
他提到了重大变化,但没有提到平均负载变化。
根据日期,我查找了内核邮件列表档案以找到实际的补丁,但可用的最早电子邮件是 1995 年 6 月,当时系统管理员写道:
“在开发一个系统以使这些邮件档案更有效地扩展时,我不小心破坏了当前的档案集(啊,哎呀)。”
我的搜索开始感到被诅咒。值得庆幸的是,我发现了一些旧的 linux-devel 邮件列表存档,这些存档是从服务器备份中拯救出来的,通常存储为摘要的 tarball。我搜索了超过 6,000 个摘要,其中包含超过 98,000 封电子邮件,其中 30,000 封来自 1993 年。但不知何故,所有这些都缺少。看起来好像原始补丁描述可能永远丢失了,“为什么”仍然是个谜。
不间断的起源
幸运的是,我终于在oldlinux.org上 1993 年的压缩邮箱文件中找到了变化。这里是:
来自:Matthias Urlichs urlichs@smurf.sub.org
主题:平均负载损坏?
日期:1993 年 10 月 29 日星期五 11:37:23 +0200

内核在计算平均负载时只计算“可运行”进程。
我不喜欢那样;问题是正在交换的进程或
等待“快速”,即不可中断的 I/O,也会消耗资源。

负载平均下降似乎有点不直观,当您
用慢速交换磁盘替换您的快速交换磁盘…

无论如何,以下补丁似乎使负载平均更多
一致 WRT 系统的主观速度。而且,最重要的是,
当没有人做任何事情时,负载仍然为零。:wink:

— 内核/sched.c.orig Fri Oct 29 10:31:11 1993
+++ 内核/sched.c 1993 年 10 月 29 日星期五 10:32:51
@@ -414,7 +414,9 @@
无符号长 nr = 0;

for(p = &LAST_TASK; p > &FIRST_TASK; --p)
  • if (*p && (*p)->state == TASK_RUNNING)
  • if (*p && ((*p)->state == TASK_RUNNING) ||
  • (*p)->状态 == TASK_UNINTERRUPTIBLE) ||
  • (*p)->状态 == TASK_SWAPPING))
    nr += FIXED_1;
    返回 nr;
    }

    Matthias Urlichs \ XLink-POP N|rnberg | 电子邮件:urlichs@smurf.sub.org
    Schleiermacherstra_e 12 \ Unix+Linux+Mac | 电话: …请使用电子邮件。
    90491 N|rnberg(德国)\咨询+网络+编程+等 42
    从近 24 年前开始阅读这种变化背后的想法真是太神奇了。
    这证实了故意更改负载平均值以反映对其他系统资源的需求,而不仅仅是 CPU。Linux 从“CPU 平均负载”变成了人们可以称之为“系统负载平均”的东西。
    他使用较慢交换磁盘的示例是有道理的:通过降低系统的性能,对系统的需求(以运行+排队衡量)应该会增加。但是,平均负载减少了,因为它们只跟踪 CPU 运行状态而不是交换状态。Matthias 认为这是不直观的,所以他修复了它。
    今天不间断
    但是,Linux 平均负载是否有时会过高,超过磁盘 I/O 可以解释的程度?是的,尽管我的猜测是这是由于使用 TASK_UNINTERRUPTIBLE 的新代码路径在 1993 年不存在。在 Linux 0.99.14 中,有 13 个代码路径直接设置了 TASK_UNINTERRUPTIBLE 或 TASK_SWAPPING(交换状态后来从 Linux 中删除)。如今,在 Linux 4.12 中,有近 400 个设置 TASK_UNINTERRUPTIBLE 的代码路径,包括一些锁定原语。这些代码路径之一可能不应该包含在负载平均值中。下次我的平均负载似乎太高时,我会看看是否是这种情况,是否可以修复。
    我(第一次)给 Matthias 发了电子邮件,询问他对将近 24 年后的平均负载变化有何看法。他在一小时内回复(正如我在推特上提到的),并写道:
    ““平均负载”的目的是从人类的角度得出一个与系统有多忙有关的数字。TASK_UNINTERRUPTIBLE 意味着(意味着?)进程正在等待诸如磁盘读取之类的东西,这有助于系统负载。一个磁盘密集型系统可能非常缓慢,但平均 TASK_RUNNING 仅为 0.1,这对任何人都没有帮助。”
    (如此迅速地得到回应,甚至得到回应,真的让我很开心。谢谢!)
    所以 Matthias 仍然认为这是有道理的,至少考虑到 TASK_UNINTERRUPTIBLE 过去的含义。
    但是今天 TASK_UNITERRUPTIBLE 匹配更多的东西。我们是否应该将平均负载更改为 CPU 和磁盘需求?调度程序维护者 Peter Zijstra 已经给我发送了一个聪明的选项来探索这样做:在平均负载中包含task_struct->in_iowait而不是 TASK_UNINTERRUPTIBLE,以便它更接近地匹配磁盘 I/O。然而,这引出了另一个问题,我们真正想要的是什么?我们是想根据线程来衡量对系统的需求,还是仅仅对物理资源的需求?如果是前者,则应包括等待不间断锁,因为系统需要这些线程。他们没有闲着。所以也许 Linux 平均负载已经按照我们想要的方式工作了。
    为了更好地理解不间断的代码路径,我想要一种在行动中测量它们的方法。然后我们可以检查不同的例子,量化他们花费的时间,看看这一切是否有意义。
    测量不间断的任务
    以下是来自生产服务器的Off-CPU 火焰图,跨越 60 秒并仅显示内核堆栈,其中我正在过滤以仅包括处于 TASK_UNINTERRUPTIBLE 状态 ( SVG ) 的那些。它提供了许多不间断代码路径的示例:
    如果您不熟悉非 CPU 火焰图:您可以单击帧放大,检查显示为帧塔的完整堆栈。x 轴大小与阻塞在 CPU 外的时间成正比,排序顺序(从左到右)没有实际意义。非 CPU 堆栈的颜色为蓝色(我对 CPU 堆栈使用暖色),饱和度具有随机变化来区分帧。
    我使用来自bcc的 offcputime 工具(该工具需要 Linux 4.8+ 的 eBPF 功能)和我的火焰图软件生成了这个:

./bcc/tools/offcputime.py -K --state 2 -f 60 > out.stacks

awk ‘{ print $1, $2 / 1000 }’ out.stacks | ./FlameGraph/flamegraph.pl --color=io --countname=ms > out.offcpu.svg b>

我正在使用 awk 将输出从微秒更改为毫秒。offcputime “–state 2” 匹配 TASK_UNINTERRUPTIBLE(参见 sched.h),这是我刚刚为这篇文章添加的一个选项。Facebook 的 Josef Bacik 首先用他的kernelscope工具做到了这一点,该工具也使用 bcc 和火焰图。在我的示例中,我只是显示内核堆栈,但 offcputime.py 也支持显示用户堆栈。
至于上面的火焰图:它显示 60 秒中只有 926 毫秒用于不间断睡眠。这只会使我们的平均负载增加 0.015。是时候在一些 cgroup 路径中了,但是这个服务器并没有做太多的磁盘 I/O。
这是一个更有趣的,这次只跨越 10 秒(SVG):
右边的宽塔在 proc_pid_cmdline_read() 中显示systemd-journal(读取 /proc/PID/cmdline),被阻塞,对负载平均贡献 0.07。左边有一个更宽的页面错误塔,它也最终出现在 rwsem_down_read_failed() 中(将 0.23 添加到平均负载)。我使用火焰图搜索功能以洋红色突出显示了这些功能。这是 rwsem_down_read_failed() 的摘录:
/* 等待获得锁 */
而(真){
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
如果 (!waiter.task)
休息;
日程();
}
这是使用 TASK_UNINTERRUPTIBLE 的锁获取代码。Linux 具有互斥量获取函数的不可中断和可中断版本(例如,mutex_lock() 与 mutex_lock_interruptible(),以及用于信号量的 down() 和 down_interruptible())。可中断版本允许任务被信号中断,然后在获得锁之前唤醒处理它。不间断锁休眠的时间通常不会对平均负载增加太多,但在这种情况下,它们会增加 0.30。如果这要高得多,那么值得分析一下是否可以减少锁争用(例如,我将开始研究 systemd-journal 和 proc_pid_cmdline_read()!),这应该会提高性能并降低平均负载。
将这些代码路径包含在平均负载中是否有意义?是的,我会这么说。这些线程正在工作,并且碰巧被锁阻塞。他们没有闲着。它们是对系统的需求,尽管需要软件资源而不是硬件资源。
分解 Linux 平均负载
Linux负载平均值可以完全分解成组件吗?这是一个例子:在一个空闲的 8 CPU 系统上,我启动了tar来归档一些未缓存的文件。它在磁盘读取上花费了几分钟大部分时间。以下是从三个不同的终端窗口收集的统计数据:
terma$ pidstat -p pgrep -x tar 60
Linux 4.9.0-rc5-virtual (bgregg-xenial-bpf-i-0b7296777a2585be1) 08/01/2017 x86_64 (8 CPU)

10:15:51 PM UID PID %usr %system %guest %CPU CPU 命令
10:16:51 PM 0 18468 2.85 29.77 0.00 32.62 3 焦油

termb$ iostat -x 60
[…]
平均 CPU:%user %nice %system %iowait %steal %idle
0.54 0.00 4.03 8.24 0.09 87.10

设备:rrqm/s wrqm/sr/sw/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvdap1 0.00 0.05 30.83 0.18 638.33 0.93 41.22 0.06 1.84 1.83 3.64 0.39 1.21
xvdb 958.18 1333.83 2045.30 499.38 60965.27 63721.67 98.00 3.97 1.56 0.31 6.67 0.24 60.47
xvdc 957.63 1333.78 2054.55 499.38 61018.87 63722.13 97.69 4.21 1.65 0.33 7.08 0.24 61.65
md0 0.00 0.00 4383.73 1991.63 121984.13 127443.80 78.25 0.00 0.00 0.00 0.00 0.00 0.00

termc$正常运行时间
22:15:50 154 天,23:20,5 个用户,平均负载:1.25、1.19、1.05
[…]
termc$正常运行时间
22:17:14 154 天,23:21,5 个用户,平均负载:1.19、1.17、1.06
我还为不间断状态 ( SVG )收集了一个 Off-CPU 火焰图:
最后一分钟的平均负载为 1.19。让我分解一下:
0.33 来自 tar 的 CPU 时间 (pidstat)
0.67 来自 tar 的不间断磁盘读取,推断(offcpu 火焰图在 0.69 有这个,我怀疑它开始收集的时间稍晚一些,并且跨越的时间范围略有不同)
0.04 来自其他 CPU 消费者(iostat 用户 + 系统,从 pidstat 减去 tar 的 CPU)
0.11 来自内核工作人员不间断磁盘 I/O 时间,刷新磁盘写入(offcpu 火焰图,左侧的两个塔)
加起来是 1.15。我仍然缺少 0.04,其中一些可能是舍入和测量间隔偏移误差,但很多可能是由于负载平均值是指数阻尼移动和,而我正在使用的其他平均值(pidstat,iostat)是正常平均值。在 1.19 之前,一分钟平均值为 1.25,因此其中一些仍将拖累我们走高。多少钱?从我之前的图表来看,在一分钟标记处,62% 的指标是从那一分钟开始的,其余的则更旧。所以 0.62 x 1.15 + 0.38 x 1.25 = 1.18。这与报告的 1.19 非常接近。
这是一个系统,其中一个线程(tar)加上更多(内核工作线程中的一些时间)正在工作,Linux 报告负载平均为 1.19,这是有道理的。如果它测量“CPU 负载平均值”,系统将报告 0.37(从 mpstat 的摘要中推断),这仅适用于 CPU 资源,但隐藏了需要超过一个线程的工作价值的事实。
我希望这个例子表明这些数字确实意味着一些经过深思熟虑的事情(CPU + 不间断),你可以分解它们并弄清楚。
理解 Linux 平均负载
我是在平均负载意味着 CPU 平均负载的操作系统中长大的,所以 Linux 版本一直困扰着我。也许真正的问题一直是“平均负载”这个词和“I/O”一样模棱两可。哪种类型的 I/O?磁盘 I/O?文件系统 I/O?网络输入/输出?…同样,哪个负载平均值?CPU平均负载?系统负载平均值?以这种方式澄清它让我这样理解它:
在 Linux 上,负载平均值是(或试图成为)“系统负载平均值”,对于整个系统,测量正在工作和等待工作的线程数(CPU、磁盘、不间断锁)。换句话说,它测量不完全空闲的线程数。优势:包括对不同资源的需求。
在其他操作系统上,负载平均值是“ CPU 负载平均值”,测量 CPU 正在运行的数量 + CPU 可运行线程。优点:可以更容易理解和推理(仅适用于 CPU)。
请注意,还有另一种可能的类型:“物理资源负载平均值”,它仅包括物理资源的负载(CPU + 磁盘)。
也许有一天我们会在 Linux 中添加额外的负载平均值,让用户选择他们想要使用的内容:单独的“CPU 负载平均值”、“磁盘负载平均值”、“网络负载平均值”等。或者只是使用不同的指标。
什么是“好”或“坏”的平均负载?

用现代工具测量的平均负载
有些人发现似乎适用于他们的系统和工作负载的值:他们知道当负载超过 X 时,应用程序延迟很高,客户开始抱怨。但这并没有真正的规则。
对于 CPU 负载平均值,可以将该值除以 CPU 计数,然后说如果该比率超过 1.0,则您正在以饱和状态运行,这可能会导致性能问题。这有点模棱两可,因为它是一个可以隐藏变化的长期平均值(至少一分钟)。一个比率为 1.5 的系统可能运行良好,而另一个比率为 1.5 且在一分钟内突发的系统可能表现不佳。
我曾经管理过一个双 CPU 的电子邮件服务器,它在白天运行的 CPU 平均负载在 11 到 16 之间(比率在 5.5 到 8 之间)。延迟是可以接受的,没有人抱怨。这是一个极端的例子:大多数系统的负载/CPU 比率仅为 2。
至于 Linux 的系统负载平均值:这些更加模糊,因为它们涵盖了不同的资源类型,所以你不能只除以 CPU 数量。它对于相对比较更有用:如果您知道系统在负载为 20 时运行良好,而现在是 40,那么是时候深入研究其他指标以查看发生了什么。
更好的指标
当 Linux 平均负载增加时,您知道您对资源(CPU、磁盘和一些锁)有更高的需求,但您不确定是哪一个。您可以使用其他指标进行说明。例如,对于 CPU:
每个 CPU 的利用率:例如,使用mpstat -P ALL 1
每个进程的 CPU 利用率:例如top、pidstat 1等。
每线程运行队列(调度程序)延迟:例如,在 /proc/PID/schedstats、delaystats、perf sched
CPU 运行队列延迟:例如,在 /proc/schedstat、perf sched、我的runqlat bcc工具中。
CPU 运行队列长度:例如,使用vmstat 1和 ‘r’ 列,或者我的 runqlen bcc 工具。
前两个是利用率指标,后三个是饱和度指标。利用率指标可用于工作负载表征,饱和指标可用于识别性能问题。最佳 CPU 饱和度指标是运行队列(或调度程序)延迟的度量:任务/线程处于可运行状态但必须等待轮到它的时间。这些允许您计算性能问题的严重程度,例如,线程花费在调度程序延迟上的时间百分比。相反,测量运行队列长度可以表明存在问题,但更难以估计量级。
schedstats 工具在 Linux 4.6 (sysctl kernel.sched_schedstats) 中成为可调整的内核,默认情况下更改为关闭。延迟记帐公开了相同的调度程序延迟指标,它在cpustat中,我只是建议将它也添加到htop中,因为这将使人们更容易使用。比从(未记录的)/proc/sched_debug 输出中抓取等待时间(调度程序延迟)指标更容易:
$ awk ‘NF > 7 { if ($1 == “task”) { if (h == 0) { print; h=1 } } else { print } }’ /proc/sched_debug
任务 PID 树键切换优先 等待时间 sum-exec sum-sleep
系统 1 5028.684564 306666 120 43.133899 48840.448980 2106893.162610 0 0 /init.scope
ksoftirqd/0 3 99071232057.573051 1109494 120 5.682347 21846.967164 2096704.183312 0 0 /
kworker/0:0H 5 99062732253.878471 9 100 0.014976 0.037737 0.000000 0 0 /
迁移/0 9 0.000000 1995690 0 0.000000 25020.580993 0.000000 0 0 /
lru-加-漏 10 28.548203 2 100 0.000000 0.002620 0.000000 0 0 /
看门狗/0 11 0.000000 3368570 0 0.000000 23989.957382 0.000000 0 0 /
cpuhp/0 12 1216.569504 6 120 0.000000 0.010958 0.000000 0 0 /
捷运58 72026342.961752 343 120 0.000000 1.471102 0.000000 0 0 /
khungtaskd 59 99071124375.968195 111514 120 0.048912 5708.875023 2054143.190593 0 0 /
[…]
码头工人 16014 247832.821522 2020884 120 95.016057 131987.990617 2298828.078531 0 0 /system.slice/docker.service
码头工人 16015 106611.777737 2961407 120 0.000000 160704.014444 0.000000 0 0 /system.slice/docker.service
码头工人 16024 101.600644 16 120 0.000000 0.915798 0.000000 0 0 /system.slice/
[…]
除了 CPU 指标,您还可以查找磁盘设备的利用率和饱和度指标。我专注于USE 方法中的这些指标,并有一个Linux 清单。
虽然有更明确的指标,但这并不意味着平均负载没有用。它们与其他指标一起成功地用于云计算微服务的扩展策略。这有助于微服务响应不同类型的负载增加、CPU 或磁盘 I/O。有了这些政策,在扩大规模(花费金钱)上犯错比不扩大规模(让客户付出代价)更安全,因此包含更多信号是可取的。如果我们扩大太多,我们将在第二天调试原因。
我一直使用平均负载的一件事是它们的历史信息。如果我被要求检查云上性能不佳的实例,然后登录并发现一分钟的平均值远低于十五分钟的平均值,这是一个很大的线索,我可能太迟才看到性能问题居住。但在转向其他指标之前,我只花几秒钟考虑负载平均值。
结论
1993 年,一位 Linux 工程师发现了一个不直观的负载平均值案例,并通过一个三行补丁将它们从“CPU 负载平均值”永久更改为人们所谓的“系统负载平均值”。他的更改包括处于不间断状态的任务,因此负载平均值反映了对磁盘资源的需求,而不仅仅是 CPU。这些系统负载平均值计算工作和等待工作的线程数,并总结为指数阻尼移动和平均值的三元组,在方程中使用 1、5 和 15 分钟作为常数。这三组数字可以让您查看负载是增加还是减少,它们的最大值可能是用于与它们自身的相对比较。
此后,不间断状态的使用在 Linux 内核中得到了发展,现在包括不间断的锁原语。如果平均负载是对运行和等待线程(而不是严格意义上的需要硬件资源的线程)的需求量度,那么它们仍然按照我们希望的方式工作。
在这篇文章中,我挖掘了 1993 年的 Linux 平均负载补丁(出奇地难找到),其中包含作者的原始解释。我还在现代 Linux 系统上使用 bcc/eBPF 测量了不间断状态下的堆栈跟踪和时间,并将这次可视化为 CPU 外火焰图。这种可视化提供了许多不间断睡眠的示例,并且可以在需要解释异常高负载平均值时生成。我还提出了可以用来更详细地了解系统负载的其他指标,而不是平均负载。
最后,我将引用调度程序维护者 Peter Zijlstra 在 Linux 源代码中kernel/sched/loadavg.c顶部的评论:

  • 此文件包含计算全局 loadavg * 数字所需的魔法位。这是一个愚蠢的数字,但人们认为它很重要。我们
    费尽心思让它在大型机器和无滴答内核上工作。
    参考
    Saltzer, J. 和 J. Gintell。“ The Instrumentation of Multics ”,CACM,1970 年 8 月(解释指数)。
    Multics system_performance_graph命令参考(提到了 1 分钟的平均值)。
    TENEX源代码(平均负载代码在 SCHED.MAC 中)。
    RFC 546 “1973 年 7 月的 TENEX 平均负载”(解释测量 CPU 需求)。
    Bobrow,D.,等人。“TENEX:PDP-10 的分页时间共享系统”,ACM 通信,1972 年 3 月(解释负载平均三元组)。
    Gunther, N. “UNIX 平均负载第 1 部分:工作原理” PDF(解释指数计算)。
    Linus 关于Linux 0.99 patchlevel 14的电子邮件。
    平均负载更改电子邮件在oldlinux.org上(在 alan-old-funet-lists/kernel.1993.gz 中,而不是在我首先搜索的 linux 目录中)。
    平均负载变化前后的 Linux kernel/sched.c 源码:0.99.13 , 0.99.14。
    Linux 0.99 版本的压缩包位于kernel.org上。
    当前 Linux 平均负载代码:loadavg.c , loadavg.h
    bcc分析工具包括我的offcputime ,用于跟踪 TASK_UNINTERRUPTIBLE。
    火焰图用于可视化不间断的路径。
    感谢 Deirdre Straughan 的编辑。

转载翻译自: https://www.brendangregg.com