Unix/Linux 系统中Load Average 的确切含义

     昨天《怪物世界》有出一个和负载相关的运营事故,具体事故我另外开贴,因为中间涉及到系统load参数异常,在网上查了下资料,感觉都没有解释清楚。最后查到英文的维基百科,终于算是完全说清楚了。
     
     我把维基的原文和我的译文一起贴出来,方便以后对这个问题有疑惑的同学查询。
 
     译文如下:
 
     在UNIX 系统中,系统的 load  是一个关于计算机计算量的指标。load average 表示在一个时间段内的系统平均负载(load),它一般显示成三个数值,代表了系统在过去一分钟,五分钟及十分钟的平均负载。
 
    UNIX系列操作系统的负载计算方法                                                                                 
     
     所有UNIX或是类UNIX的操作系统,都会产生三个关于平均负载(load average)的数值指标。用户能通过在UNIX shell下,使用uptime 这个指令,很方便地获得这三个数值。
    $ uptime
 14:34:03 up 10:43,  4 users,  load average: 0.06, 0.11, 0.09
 
     使用 w 或是 top 命令也会显示这三个数值,很多其他的基于图形界面的监控工具也会有显示。
     在Linux系统中,我们也可以通过访问/proc/loadavg文件查看到这三个数值。
      
     在一个空闲的机器中,load数据为0。当有进程在使用或是等待CPU(这意味着这个进程在运行队列,或是在等待队列中,两者必居其一),则load数据加1。
 
     在许多UNIX系统中,只会计算正处于运行(正在使用CPU)或是等待(等待CPU资源)状态的进程。但是在Linux中,也会把处于 “不被打断的睡眠(uninterruptible sleep"状态的进程计算进去。所谓"不被打断的睡眠”状态,通常是指正在等待磁盘等IO设备。Linux的这种计算方法,当系统的很多进程都都阻塞在一个繁忙或是被挂起的IO系统上时,会导致结果有很大的不同。举例来说,这种情况可能会发生在进程被阻塞在一个NFS (挂载的网络文件系统)服务失败或是一个慢速媒介(如 USB1.X标准的存储设备)上。这些情况会体现在平均负载上,但却不会体现在CPU使用率上(不过这仍然取决于进程需要等待多长时间)。
          
     系统会使用指数移动加权平均算法( exponentially damped/weighted moving)计算平均负载,负载的这三个数值分别代表过去一分钟,五分钟,十五分钟的计算结果。
     
     对一个单CPU的系统来说,数值是直接和这单个CPU的计算能力相关的,我们可以把平均负载的数值直接看成是整个系统在这个时间段内的利用率(意味着,数值如果为1,则利用率为100%)。对一个多核系统来说,我们需要把得到的负载除以CPU的核心数,来得到一个可比较的系统利用率百分比。
     
     举例来说,在一个单核系统上,对平均负载"1.73 0.50 7.98”我们可以这样理解:
     *在过去的一分钟里,CPU有73%的过载( 在这个过程中,1个CPU要处理1.73个可运行进程的请求,所以有0.73个进程需要排队等待CPU资源)
     *在过去的五分钟里,CPU有50%的空载( 在这个过程中,没有进程需要等待CPU资源)
     *在过去的十五分钟里,CPU 有698%的过载( 在这个过程中,1个CPU要处理7.98个可运行进程的请求,所以有6.98个进程需要排队等待CPU资源)
 
     这意味着,对于过去的一分钟而言,如果这个CPU有实际的1.73倍那么快的速度,或是如果有两倍 的CPU核 数(实际是1.73倍 )的话,系统就可以完全处理掉所有在请求队列中的处理请求。而对于过于的五分钟而言,CPU比实际让所有可执行的线程陷入等待所需的速度快了两倍 。
     对于一个四核系统,平均负载3.73意味着,平均下来有3.73个进程准备执行(译注:按最上面的解释应该是正在运行),没有进程需要等待CPU资源(CPU资源为4)。
     
     在现代UNIX系统中,如何处理线程在平均负载中的计算是因不同的操作系统而异的。有些系统在计算时,把线程和进程同等对待:每一个等待CPU资源的线程,会让负载加1。而另外一些系统,比如说一些实现了超线程(M:N threading,M个用户线程,对应N个内核线程)的系统,会使用不同的策略,比如说有些做法是每个进程只会计算一次(译注,意思是如果这个进程有多于一个线程等待CPU资源,也只会让负载加1),而另一些做法是只计算那些已经从用户线程队列,抛送到内核线程队列的线程(译注,意思是只有当这个线程,已经是一个内核线程,并且在等待或是使用CPU资源,才会让负载加1),具体策略可能取决于进程的并行策略设置(which may depend on the level of concurrency set on the process,此句存疑)。
     
     很多系统采用定期取样任务队列的方法来计算平均负载,而不是采用每次调度请求事件产生时做重新计算。采用定期采样的做法主要是为了减少性能开销,因为调度请求事件产生的非常频繁,而调度策略的计算效率会非常大的影响整个系统的效率。因为这个原因,采样出错就会导致平均负载的计算精度受影响,这取决于具体的系统实现。这导致一个问题,如果一个程序会定期被唤醒,而这个唤醒的时间点,刚好和平均负载的取样时间间隔开来,在这种情况下,这个进程在负载中的数值会比实际的更大,或是更小。
 
     CPU 负载 与 CPU 使用率( CPU load vs CPU utilization )                                                                    
     Ferrari et al曾做过关于CPU不同负载指标的比较研究。他指出,基于CPU请求队列的负载数据,在用于负载均衡的比较时,会优于基于CPU 使用率的方法。原因在于,当一个CPU的负载已经很高时,CPU的利用率一般会接近100%,这个数据不能反映出准确的使用负载。比之相比,CPU的请求队列长度能直接反映出一个CPU的整个负载(译注:当CPU的负载越过1时,CPU utilization的数据会一直是100%,但我们并不会知道具体的负载已经到多高了)。举个例子,两个系统,一个请求队列内有3个请求,而另一个有6个请求,都有可能有接近100%的CPU使用率,而实际上他们的负载有明显的不同。
 
     测算CPU负载                                                                                                         
     在Linux系统中,平均负载不是每个时钟周期都会做计算的,而是由一个变量来驱动,这个变量基于某一个设定的频率,并在每一个时钟周期会做测试,看是否要做计算。(频率值 Hz Variable 是特定的Linux系统的激活周期,1HZ 等于一个时钟周期,一般设为10毫秒。)尽管祯率值在某些版本的Linux下能够做设置,但一般会被设置成100。(译注,即100*10毫秒,即1秒钟计算一次)。计算代码会使用祯率值去决定CPU负载的计算频率。特别指出,函数 timer.c::calc_load() 会每5个周期计算一次,即大概每5秒计算一次。下面是这个函数的实现:
 
unsigned long avenrun[3];
 
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);
   }}
 
 
     avenrun 数组包含了1分钟,5分钟及15分钟的平均值,宏 CALC_LOAD  及其相关的数值定义在sched.h文件内:
 
#define FSHIFT   11             /* nr of bits of precision */

#define FIXED_1 (1<<FSHIFT) /* 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;

 

 
     维基百科原文地址:
posted @ 2012-09-07 23:11  肉松  阅读(1210)  评论(0)    收藏  举报