UNIX的平均负载 第一部分 :如何工作

黄平俊 发布于 2009/04/20 16:06
阅读 664
收藏 1

UNIX® 的平均负载 第一部分 :如何工作

作者:Dr. Neil Gunther,
Performance Dynamics Company

为了正确查看数学符号,请检查这里 ,然后再继续。

 

你曾经想知道这三个在UNIX®平均负荷(LA)报告中出现的数字是如何计算出来的?

这个TeamQuest在线栏说明如何做以及如何重组平均负载(LA)可以得到更好的容量规划。 但首先,请测试您的知识,在平均负载三胞胎"LA Triplets"测验。

 

 

 


在这两个系列部分我要探索平均数在性能分析与容量规划中的作用。 平均数有许多表现形式,例如,算术平均(通常使用的) ,移动平均线(通常在财务规划中采用) ,几何平均数(用于规格CPU的性能衡量 ) ,调和平均数(没有足够应用) ,等等。

更重要的是,我们将看到随时间推移的平均数或者依赖时间的平均数 一个依赖时间的平均数的具体例子是在某些Unix命令中出现的平均负荷指标(load average metric) 在第1部分我们将看看平均负荷是如何被计算出来的。 第2部分中我将它与其他均适用于容量规划和性能分析的技术进行比较。 本文假设你不熟悉Unix命令,因此我将首先回顾一些命令,它们都能显示平均负荷指标。直到第4部分 ,我们会投入到UNIX的内核代码中去,并且完成所有的工作。

1 Unix命令

其实,平均负载不是一个传统意义中的UNIX命令。相反,它是一个嵌入式的指标,显示在其他Unix命令如uptime和procinfo的输出中。这些命令通常是被UNIX的系统管理员用来观察系统资源消耗的。 让我们来仔细看看其中一些命令。

 1.1经典输出

普通的ASCII文本格式出现在各种UNIX的命令中。 以下是一些常见的例子。

uptime

这个uptime外壳命令产生下列输出:

[pax:~]% uptime
9:40am up 9 days, 10:36, 4 users, load average: 0.02, 0.01, 0.00

它显示自从上次系统重启以来,活动的用户进程数量和所谓的平均负荷指标(load average)。

procinfo

在Linux系统上,procinfo命令产生以下输出:

[pax:~]% procinfo
Linux 2.0.36 (root@pax) (gcc 2.7.2.3) #1 Wed Jul 25 21:40:16 EST 2001 [pax]

Memory: Total Used Free Shared Buffers Cached
Mem: 95564 90252 5312 31412 33104 26412
Swap: 68508 0 68508

Bootup: Sun Jul 21 15:21:15 2002 Load average: 0.15 0.03 0.01 2/58 8557
...

平均负载指标出现在这个输出的左下角。

w

w(ho)命令产生下列输出:

 [pax:~]% w
  9:40am  up 9 days, 10:35,  4 users,  load average: 0.02, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
mir ttyp0 :0.0 Fri10pm 3days 0.09s 0.09s bash
neil ttyp2 12-35-86-1.ea.co 9:40am 0.00s 0.29s 0.15s w
...

请注意,第一行的输出与uptime命令的输出相同。

top

top命令是最近加入到UNIX命令集中的,它通过计算进程消耗CPU的时间来给进程排名。它产生下列输出:

  4:09am  up 12:48,  1 user,  load average: 0.02, 0.27, 0.17
58 processes: 57 sleeping, 1 running, 0 zombie, 0 stopped
CPU states: 0.5% user, 0.9% system, 0.0% nice, 98.5% idle
Mem: 95564K av, 78704K used, 16860K free, 32836K shrd, 40132K buff
Swap: 68508K av, 0K used, 68508K free 14508K cched

PID USER PRI NI SIZE RSS SHARE STAT LIB %CPU %MEM TIME COMMAND
5909 neil 13 0 720 720 552 R 0 1.5 0.7 0:01 top
1 root 0 0 396 396 328 S 0 0.0 0.4 0:02 init
2 root 0 0 0 0 0 SW 0 0.0 0.0 0:00 kflushd
3 root -12 -12 0 0 0 SW< 0 0.0 0.0 0:00 kswapd
...

所有这些命令,请注意,输出中都有三个数字报告平均负载。相当普遍的是,这些数字显示出从左至右的降序但是有时,又是升序排列,正如上面的输出。

 1.2 图形输出

平均负荷也可以显示为一个时间序列一样,在这里显示的是工具ORCA的输出。

 

LAdaily.gif
图1 :ORCA3日平均负载图。

虽然这种直观辅助工具帮助我们看到,绿色的曲线更spikey和比红色曲线变化更大,它使我们能够看到一天的完整的数据,目前尚不清楚这对能力规划或性能分析有多大作用。 我们需要了解的是平均负载指标的定义和计算方法。

 2 它是什么?

那么,这些不同的命令确切地说明了什么是平均负载么? 让我们来看看官方的UNIX文档。

2.1 UNIX操作手册

[pax:~]% man "load average"
No manual entry for load average

哎呀!没有手册说明!平均负载是其他命令的内嵌输出,所以没有单独的手册文档。好吧,让我们看看uptime命令的文档,能否从中获得写帮助。

...
DESCRIPTION
uptime gives a one line display of the following informa-
tion. The current time, how long the system has been run-
ning, how many users are currently logged on, and the sys-
tem load averages for the past 1, 5, and 15 minutes.
...
uptime产生一条信息,包括当前时间,系统连续运行时间,当前登录用户
数量和过去1、5及15分钟内的系统平均负载。

所以那三个数字指标,表示了“过去1、5、15分钟内的系统平均负载。”

他们是绿色 , 蓝色红色曲线,分别标在图1上。

但是,文档中仍然回避了什么是负载(load)的问题。

2.2 什么是大师不得不说的

让我们转向一些UNIX热点问题来看一看。

Tim O'Reilly and Crew

《UNIX超级工具》(UNIX Power Tools [])这本书,在726页告诉我们,CPU是:

 

The load average tries to measure the number of active processes at any time. As a measure of CPU utilization, the load average is simplistic, poorly defined, but far from useless.

平均负载试图衡量任何时间内的活动进程数量。在被当作CPU利用率的衡量标准时,虽然平均负载数是简单的和缺乏定义的,但远不是无用的。

这是令人鼓舞的!无论如何,它帮助我们解释了衡量的内容:活动进程数量。在720页 39.07 检查系统的负载:uptime 中这样说:

... High load averages usually mean that the system is being used heavily and the response time is correspondingly slow.

What's high? ... Ideally, you'd like a load average under, say, 3, ... Ultimately, 'high' means high enough so that you don't need uptime to tell you that the system is overloaded.

高平均负载通常表示系统任务繁重,因而响应时间变慢。
高平均负载是多少?一般来说,要根据系统来判断。理想情况下,总是希望平均负载低一点,3就比较合适。基本上来说,这里的“高”是指足够高,而不需要命令uptime来指出系统已经超载。

嗯... 这个数字“ 3 ”是怎么来的? 和这三个平均数( 1 , 5 , 15分钟) ,他们指的是什么?

Adrian Cockcroft 在 Solaris

Sun性能和调优(Sun Performance and Tuning)[]中的97页中的一段:搞懂和使用平均负载,Adrian Cockcroft叙述到:

The load average is the sum of the run queue length and the number of jobs currently running on the CPUs. In Solaris 2.0 and 2.2 the load average did not include the running jobs but this bug was fixed in Solaris 2.3.

平均负载是系统等待运行队列的长度与当前所有CPU中正在运行的工作的和。在Solaris 2.0 及 2.2中,平均负载并不包含正在执行的工作,但这个缺陷在Solaris 2.3中得到了修复。

所以,即便是Sun的“大男孩们”做错了什么。然而,这样的想法,将平均负载与CPU运行队列联系在一起是重要的观点。

O'Reilly 等. 还注意到一些使用平均负载的潜在陷阱。

...different systems will behave differently under the same load average. ... running a single cpu-bound background job .... can bring response to a crawl even though the load avg remains quite low.

……不同的系统在相同平均负载下的表现不同。……运行一个绑定CPU的后台工作……虽然平均负载很低,也能将反应拖得很慢。

正如我将证明的,这取决于你何时去看。如果这个绑定CPU的进程运行足够长的时间,它会驱使平均负载升高,因为这个这个进程一直处在正在执行或可以执行状态。这个事实默默地指出,平均负载并不是你认为的平均。正如我们上述介绍的,这是一个时间依赖的平均水平。不仅如此,它是一种有阻尼的时间依赖性的平均水平。 要了解更多信息,让我们做一些对照实验。
3 性能试验
本节的实验涉及到在单CPU的linux系统中运行一些后台负载工作。实验共两个阶段,历时一个小时。

CPU被挂钩2100秒后进程被杀死 CPU在余下的1500秒后保持静默

一个Perl脚本将每5分钟使用一次uptime命令记录平均负载。这是详细信息。
3.1 负载测试
两个非常繁忙的循环处理将在一个单CPU的Linux系统的后台运行。测试分两个阶段:
1.CPU被这些工作拖住了2100秒。
2.CPU相对平静了1500秒。
1分钟平均数在进入测试300秒后达到2。5分钟平均数在1200秒时达到2,15分钟平均数在大约3600秒后到达2,但是进程在35分钟(2100秒)后被杀死。

3.2 采样过程

正如作者[]关于Linux内核的解释,因为我们的测试进程是CPU绑定的,他们将处在TASK_RUNNING状态。 这意味着它们是:

  • 正在运行 ,即目前正在CPU中执行
  • 可以运行 ,即在CPU的run_queue中等待被运行

Linux内核还检查是否有任何任务处在短期睡眠状态,称为TASK_UNINTERRUPTIBLE状态 如果有,它们也包括在平均负载的采样中。它们都没有在我们的负载测试中。

下面的源代码片段显示如何做到这一点的更详细资料。

600  * Nr of active tasks - counted in fixed-point numbers
601 */
602 static unsigned long count_active_tasks(void)
603 {
604 struct task_struct *p;
605 unsigned long nr = 0;
606
607 read_lock(&tasklist_lock);
608 for_each_task(p) {
609 if ((p->state == TASK_RUNNING ||
610 (p->state & TASK_UNINTERRUPTIBLE)))
611 nr += FIXED_1;
612 }
613 read_unlock(&tasklist_lock);
614 return nr;
615 }

所以,uptime每5秒钟的采样是Linux内核的内在时基更新平均负载的计算。

 

3.3 测试结果

实验结果的图示在图2。图中的颜色并不与图1中的具有相同的意思。


 LAFull.gif 

图 2: Linux 平均负载测试结果。 

相比之下,这是如何一个单独繁忙循环运行在一个单CPU的Solaris系统中的图示。


LASolaris.gif 
Figure 3: Solaris 平均负载测试结果。

你应该原谅这个跳出来的“负载”就是CPU利用率的结论。 随着Linux的测试结果表明,当两个繁忙的进程在运行,在单个CPU的最高负载是2(而不是1 )。因此,负载等于CPU利用率。

从另一个角度来看,图2类似于电容器的充电和放电过程

 

capacitor.gif 
图 4: 电容的充电(charge)及放电(discharge)过程.

 

4 内核魔术

An Addendum

现在让我们进入Linux内核 ,看看这些平均负载的数字是如何产生的。

unsigned long avenrun[3];
624
625 static inline void calc_load(unsigned long ticks)
626 {
627 unsigned long active_tasks; /* fixed-point */
628 static int count = LOAD_FREQ;
629
630 count -= ticks;
631 if (count < 0) {
632 count += LOAD_FREQ;
633 active_tasks = count_active_tasks();
634 CALC_LOAD(avenrun[0], EXP_1, active_tasks);
635 CALC_LOAD(avenrun[1], EXP_5, active_tasks);
636 CALC_LOAD(avenrun[2], EXP_15, active_tasks);
637 }
638 }

倒计时是在5赫兹的LOAD_FREQ中进行的,这是多长时间呢?

  1 HZ    =   100 ticks
5 HZ = 500 ticks
1 tick = 10 milliseconds
500 ticks = 5000 milliseconds (or 5 seconds)

所以,5赫兹指CALC_LOAD每5秒钟被调用一次。

  

4.1 魔术数字

函数CALC_LOAD其实是一个定义在sched.h文件中的宏。

58 extern unsigned long avenrun[];         /* Load averages */
59
60 #define FSHIFT 11 /* nr of bits of precision */
61 #define FIXED_1 (1<
62 #define LOAD_FREQ (5*HZ) /* 5 sec intervals */
63 #define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
64 #define EXP_5 2014 /* 1/exp(5sec/5min) */
65 #define EXP_15 2037 /* 1/exp(5sec/15min) */
66
67 #define CALC_LOAD(load,exp,n) \
68 load *= exp; \
69 load += n*(FIXED_1-exp); \
70 load >>= FSHIFT;

一个值得注意并令人好奇的是这些数字: 1884, 2014, 2037。 代表什么? 如果我们看一下这段代码的开头,我们会知道:

/*
49 * These are the constant used to fake the fixed-point load-average
50 * counting. Some notes:
51 * - 11 bit fractions expand to 22 bits by the multiplies: this gives
52 * a load-average precision of 10 bits integer + 11 bits fractional
53 * - if you want to count load-averages more often, you need more
54 * precision, or rounding will get you. With 2-second counting freq,
55 * the EXP_n values would be 1981, 2034 and 2043 if still using only
56 * 11 bit fractions.
57 */
这是用来假冒定点平均负载计数的常量。
11位小数通过乘法扩大到22位:这样有了一个精度为10位整数和11为小数的平均负载数。
如果你想更频繁地记录平均负载,你就需要更大的精度,否则会四舍五入。使用2秒的计数
周期,如果仍然使用11位小数,EXP_n的数值将是1981,2034和2043。

这些魔术数字是使用定点(而不是浮点)的结果。

使用1分钟取样作为一个例子,转换的exp(5/60)到11位精度的二进制是这样的:

 

e5 / 60 � e5 / 60
211
 
(1)

但是EXP_M表示了逆运算exp(-5/60)。因此,我们能从下面的公式中直接计算出那些魔术数字。

EXP_M = 211
2 5 log2(e) / 60M
 
(2)

M=1代表分钟采样周期。表1整理了部分结果。

T EXP_T Rounded
5/60 1884.25 1884
5/300 2014.15 2014
5/900 2036.65 2037
2/60 1980.86 1981
2/300 2034.39 2034
2/900 2043.45 2043

表 1: 平均负载的魔术数字

这些数字在上述的内核注释中得到了充分的认同。使用定点运算大概是出于性能考虑,因为计算能在内核空间中进行,而不是用户空间。

仍然有一个问题,像exp(5/60)这样的比值是如何来的?

  

4.2 魔法揭示

用1分钟平均数举例,CALC_LOAD等同于数学公式:

load(t) = load(t-1) e-5/60 + n (1 - e-5/60)
(3)

如果我们认为n=0,公式(3) 简化为:

load(t) = load(t-1) e-5/60
(4)

如果我们迭代公式(4),在t = t0 和 t = T 之间,我们得到:

load(tT) = load(t0) e-5t/60
(5)

这就是指数衰变,就像我们在图2中看到的从时间t0 = 2100到tT = 3600的一样。

Conversely, when n = 2 as it was in our experiments, the load average is dominated by the second term such that:

相反,就像在我们的实验中一样,当n=2时,平均负载被第二种因素主宰了:

load(tT) = 2 load(t0) (1 - e-5t/60)
(6)

这是一个单调增函数,就像我们在图2中的t0 = 0到tT= 2100中看到的一样。

  

5 综述

 

因此,我们学到了什么? 那三个平均负载三胞胎(LA Triplets)中无伤大雅的数字背后有着令人惊奇的奥秘。

这三个数字为您提供某种形式的有关系统在最近的过去(1分钟),过去( 5分钟)和遥远的过去( 15分钟)所做事情的一些信息。

你如果试过了(LA Triplets)问答就会发现这样的问题:

  1. 负载不是利用率,而是任务队列长度。
  2. 他们指出了3种不同的样本时间序列。
  3. 他们是成倍阻尼的移动平均线。
  4. 他们使用了错误的顺序来代表趋势信息。

如果你试图使用它们进行容量规划,这些继承的限制是非常重要的。我会有更详细的解释在平均负载第二部分:不是你的平均的平均数。

 

参考文献

 

[BC01]D. P. Bovet and M. Cesati. Understanding the Linux Kernel. O'Reilly & Assoc. Inc., Sebastopol, California, 2001.  

[Coc95]A. Cockcroft. Sun Performance and Tuning. SunSoft Press, Mountain View, California, 1st edition, 1995.  

[Gun01]N. J. Gunther. Performance and scalability models for a hypergrowth e-Commerce Web site. In R. Dumke, C. Rautenstrauch, A. Schmietendorf, and A. Scholz, editors,Performance Engineering: State of the Art and Current Trends, volume # 2047, pages 267-282. Springer-Verlag, Heidelberg, 2001.  

[POL97]J. Peek, T. O'Reilly, and M. Loukides. UNIX Power Tools. O'Reilly & Assoc. Inc., Sebastopol, California, 2nd edition, 1997. 

译文地址:http://www.yeeyan.com/articles/view/79184/37314

原文:http://www.teamquest.com/resources/gunther/display/5/index.htm

加载中
0
小卒过河
小卒过河

学习了,好东西。

谢谢。Cool

返回顶部
顶部