关于linux:一文说清linux-system-load

7次阅读

共计 4791 个字符,预计需要花费 12 分钟才能阅读完成。

简介:双十一压测过程中,常见的问题之一就是 load 飙高,通常这个时候业务上都有受影响,比方服务 rt 飙高,比方机器无奈登录,比方机器上执行命令 hang 住等等。本文就来说说,什么是 load,load 是怎么计算的,什么状况下 load 会飙高,load 飙高是不是必然业务受影响。

作者 | 蒋冲
起源 | 阿里技术公众号

双十一压测过程中,常见的问题之一就是 load 飙高,通常这个时候业务上都有受影响,比方服务 rt 飙高,比方机器无奈登录,比方机器上执行命令 hang 住等等。本文就来说说,什么是 load,load 是怎么计算的,什么状况下 load 会飙高,load 飙高是不是必然业务受影响。

一 什么是 load

咱们平时所讲的 load,其全称是 Linux system load averages,即 linux 零碎负载平均值。留神两个关键词:一个是“负载”,它掂量的是 task(linux 内核中用于形容一个过程或者线程)对系统的需要(CPU、内存、IO 等等),第二个关键词是“均匀”,它计算的是一段时间内的平均值,别离为 1、5 和 15 分钟值。system load average 由内核负载计算并记录在 /proc/loadavg 文件中,用户态的工具(比方 uptime,top 等等)读的都是这个文件。

咱们个别认为:

  • 如果 load 靠近 0,意味着零碎处于闲暇状态
  • 如果 1min 平均值高于 5min 或 15min 平均值,则负载正在减少
  • 如果 1min 平均值低于 5min 或 15min 平均值,则负载正在缩小
  • 如果它们高于零碎 CPU 的数量,那么零碎很可能遇到了性能问题(视状况而定)

二 如何计算 load

1 外围算法

坦率了不装了,外围算法其实就是指数加权挪动平均法(Exponential Weighted Moving Average,EMWA),简略示意就是:

a1 = a0 factor + a (1 – factor),其中 a0 是上一时刻的值,a1 是以后时刻的值,factor 是一个系数,取值范畴是[0,1],a 是以后时刻的某个指标采样值。

为什么要采纳指数挪动加权平均法?我集体了解

1、指数挪动加权平均法,是指各数值的加权系数随工夫呈指数式递加,越凑近以后时刻的数值加权系数就越大,更能反映近期变动的趋势;

2、计算时不须要保留过来所有的数值,这对内核十分重要。

咱们来看看,内核是怎么计算 load average 的,以下简称 load。

下面的指数挪动均匀公式,a1 = a0 e + a (1 – e),具体到 linux load 的计算,a0 是上一时刻的 load,a1 是以后时刻的 load,e 是一个常量系数,a 是以后时刻的 active 的过程 / 线程数量。

如上一节所述,linux 内核计算了三个 load 值,别离是 1 分钟 / 5 分钟 /15 分钟 load。计算这三个 load 值时,应用了三个不同的常量系数 e,定义如下:

#define EXP_1 1884       /* 1/exp(5sec/1min) */
#define EXP_5 2014      /* 1/exp(5sec/5min) */
#define EXP_15 2037   /* 1/exp(5sec/15min) */

这三个系数是怎么来的呢?公式如下:

  • 1884 = 2048/(power(e,(5/(601)))) / e = 2.71828 */
  • 2014 = 2048/(power(e,(5/(60*5))))
  • 2037 = 2048/(power(e,(5/(60*15))))

其中 e =2.71828,其实就是天然常数 e,也叫欧拉数(Euler number)。

那为什么是这么个公式呢?其中,5 是指每五秒采样一次,60 是指每分钟 60 秒,1、5、15 则别离是 1 分钟、5 分钟和 15 分钟。至于为什么是 2048 和天然常数 e,这里波及到定点计算以及其余一些数学知识,不是咱们钻研的重点,临时不展开讨论。

咱们看看内核中理论代码:

/*
 * a1 = a0 * e + a * (1 - e)
 */     
static inline unsigned long
calc_load(unsigned long load, unsigned long exp, unsigned long active)
{       
        unsigned long newload;
        // FIXED_1 = 2048
        newload = load * exp + active * (FIXED_1 - exp);
        if (active >= load)
                newload += FIXED_1-1;

        return newload / FIXED_1;
}

就是一个很直观的实现。下面代码中,第一个参数就是上一时刻的 load,第二个参数就是常量系数,第三个参数是 active 的过程 / 线程数量(包含 runnable 和 uninterruptible)。

2 计算流程

load 的计算分为两个步骤:

1、周期性地更新每个 CPU 上的 rq 里的 active tasks,包含 runnable 状态和 uninterruptible 状态的 task,累加到一个全局变量 calc_load_tasks。

2、周期性地计算 load,load 的计算次要就是基于上述 calc_load_tasks 变量。

第一个步骤,每个 cpu 都必须更新 calc_load_tasks,然而第二个步骤只由一个 cpu 来实现,这个 cpu 叫 tick_do_timer_cpu,由它执行 do_timer() -> calc_global_load()计算零碎负载。

整体流程如下图所示,在每个 tick 到来时(时钟中断),执行以下逻辑:

上图中,棕色的 calc_global_load_tick 函数就是实现第一个步骤的,绿色的 calc_global_load 是实现第二个步骤,蓝色的 calc_load 就是上一节中形容的外围算法。

这里须要阐明的是,calc_global_load 把计算出来的 load 值放在一个全局的变量 avenrun 中,它的定义是 unsigned long avenrun[3],size 是 3,用于寄存 1 /5/15 分钟的 load。当查看 /proc/loadavg 的时候,就是从这个 avenrun 数组中获取数据。

三 load 高常见起因

从上述 load 的计算原理能够看出,导致 load 飙高的起因,说简略也简略,无非就是 runnable 或者 uninterruptible 的 task 增多了。然而说简单也简单,因为导致 task 进入 uninterruptible 状态的门路十分多(粗略统计,可能有 400-500 条门路)。集体感觉,有些中央有点滥用这个状态了。

自己基于多年的 linux 内核开发和疑难问题排查教训,总结了一些教训,以飨读者。

1 周期性飙高

已经有些业务方遇到过 load 周期性飙高的景象,如果不是因为业务上的确有周期性的峰值,那么大概率是踩中了内核计算 load 时的 bug。这个 bug 和内核的 load 采样频率(LOAD_FREQ)无关,具体细节不展开讨论。这个 bug 在 ali2016,ali3000,ali4000 中曾经修复。

排除这个起因的话,能够接着查看是否磁盘 IO 的起因。

2 IO 起因

磁盘性能瓶颈

iostat -dx 1 能够查看所有磁盘的 IO 负载状况,当 IOPS 或者 BW 高时,磁盘成为性能瓶颈,大量线程因为期待 IO 而处于 uninterruptible 状态,导致 load 飙高。此时如果用 vmstat 查看,可能会察看到 b 这一列的数值飙高,cpu iowait 飙高,/proc/stat 文件中的 procs_blocked 数值飙高。

云盘异样

云盘是虚拟盘,IO 门路长而简单,比拟容易出问题。常见的异样是 IO UTIL 100%,avgqu-sz 始终不为 0,至多有 1。大家不要误会,io util 100% 并不意味着磁盘很忙,而只意味着这个设施的申请队列里在每次采样时都发现有未实现的 IO 申请,所以当某种原因导致 IO 失落的话,云盘就会呈现 UTIL 100%, 而 ECS 内核里的 jbd2 线程,业务线程也会被 D 住,导致 load 飙高。

JBD2 bug

JBD2 是 ext4 文件系统的日志零碎,一旦 jbd2 内核线程因为 bug hang 住,所有的磁盘 IO 申请都会被阻塞,大量线程进入 uninterruptible 状态,导致 load 飙高。

排除 IO 起因之后,接着能够查看内存状况。

3 内存起因

内存回收

task 在申请内存的时候,可能会触发内存回收,如果触发的是间接内存回收,那对性能的挫伤很大。以后 task 会被阻塞直到内存回收实现,新的申请可能会导致 task 数量减少(比方 HSF 线程池扩容),load 就会飙高。能够通过 tsar –cpu –mem –load -i1 -l 查看,个别会察看到 sys cpu 飙高,cache 突降等景象。

内存带宽竞争

大家可能只据说过 IO 带宽,网络带宽,很少留神内存带宽。其实内存除了在容量维度有瓶颈,在带宽层面也有瓶颈,只是这个指标一般的工具察看不了。咱们开发的 aprof 工具能够察看内存带宽竞争,在双十一保障期间在混部环境大显神威。

4 锁

通常是内核某些门路上的 spin_lock 会成为瓶颈,尤其是网络的收发包门路上。能够用 perf top -g 查看到 spin_lock 的热点,而后依据函数地址找到内核的源码。随同的景象可能有 sys 飙高,softirq 飙高。

另外,采纳 mutex_lock 进行并发管制的门路上,一旦有 task 拿着 lock 不开释,其余的 task 就会以 TASK_UNINTERRUPTIBLE 的状态期待,也会引起 load 飙高。然而如果这把锁不在要害门路上,那么对业务可能就没啥影响。

5 user CPU

有些状况下 load 飙高是业务的失常体现,此时个别体现为 user cpu 飙高,vmstat 看到 r 这一列升高,tsar –load -i1 -l 看到 runq 升高,查看 proc/pid/schedstats 可能会看到第二个数字也就是 sched delay 会减少很快。

四 根因剖析大招

1 RUNNABLE 型 load 飙高剖析

如上所述,这种状况,通常是因为业务量的减少导致的,属于失常景象,但也有是业务代码 bug 导致的,比方长循环甚至死循环。但无论哪一种,个别都能够通过热点剖析或者叫 on cpu 剖析找到起因。on cpu 剖析的工具比拟多,比方 perf,比方阿里自研的 ali-diagnose perf 等等。

2 UNINTERRUPTIBLE 型 load 飙高剖析

所谓 UNINTERRUPTIBLE,就是意味着在等,所以咱们只有找到等在哪里,就根本找到起因了。

查找 UNINTERRUPTIBLE 状态过程

UNINTERRUPTIBLE,通常也称为 D 状态,下文就用 D 状态来形容。有一些简略的工具能够统计以后 D 状态过程的数量,略微简单一点的工具能够把 D 状态过程的调用链也就是 stack 输入。这类工具个别都是从内核提供的 proc 文件系统取数。

查看 /proc/${pid}/stat 以及 /proc/${pid}/task/${pid}/stat 文件,能够判断哪些 task 处于 D 状态,如下所示:

第三个字段就是 task 的状态。而后再查看 /proc/${pid}/stack 文件就能够晓得 task 等在哪里。如:

但有时候,D 状态的 task 不固定,这会导致抓不到 D 状态或者抓到 stack 的不精确。这时候,就得上另一个终极大招,提早剖析。

提早剖析

提早剖析须要深刻内核外部,在内核门路上埋点取数。所以这类工具的实质是内核 probe,包含 systemtap,kprobe,ebpf 等等。然而 probe 技术必须联合常识和教训能力打造成一个实用的工具。阿里自研的 ali-diagnose 能够进行各种 delay 剖析,irq_delay, sys_delay, sched_delay, io_delay, load-monitor。

五 总结

linux 内核是一个简单的并发零碎,各模块关系盘根错节。然而就 load 而言,只有从 runnable task 和 uninterruptible task 两个维度进行剖析,总能找到本源。

原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0