找到Linux虚机Load高的"元凶"

本文深入探讨了Linux系统中Load平均值的计算原理,解释了Load平均值与CPU使用率的区别,提供了查找导致Load升高的进程的方法,并分享了实用的脚本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

问题描述

有客户反馈他们的一台ECS周期性地load升高,他们的业务流量并没有上升,需要我们排查是什么原因造成的,是否因为底层异常?

要弄清Linux虚机load高,我们要搞清楚Linux top命令中Load的含义。

Load average的值从何而来

在使用top命令检查系统负载的时候,可以看到Load averages字段,但是这个字段并不是表示CPU的繁忙程度,而是度量系统整体负载。

Load averages采样是从/proc/loadavg中获取的:

0.00 0.01 0.05 1/161 29703
每个值的含义依次为:
lavg_1 (0.00) 1-分钟平均负载
lavg_5 (0.01) 5-分钟平均负载
lavg_15(0.05) 15-分钟平均负载
nr_running (1) 在采样时刻,运行队列的任务的数目,与/proc/stat的procs_running表示相同意思,这个数值是当前可运行的内核调度对象(进程,线程)。
nr_threads (161) 在采样时刻,系统中活跃的任务的个数(不包括运行已经结束的任务),即这个数值表示当前存在系统中的内核可调度对象的数量。
last_pid(29703) 系统最近创建的进程的PID,包括轻量级进程,即线程。
假设当前有两个CPU,则每个CPU的当前任务数为0.00/2=0.00

如果你看到load average数值是10,则表明平均有10个进程在运行或等待状态。有可能系统有很高的负载但是CPU使用率却很低,或者负载很低而CPU利用率很高,因为这两者没有直接关系。
Linux 源码中关于这一块的说明:
static int loadavg_proc_show(struct seq_file *m, void *v)
{
        unsigned long avnrun[3];

        get_avenrun(avnrun, FIXED_1/200, 0);

        seq_printf(m, "%lu.%02lu %lu.%02lu %lu.%02lu %ld/%d %d\n",
                LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0]),
                LOAD_INT(avnrun[1]), LOAD_FRAC(avnrun[1]),
                LOAD_INT(avnrun[2]), LOAD_FRAC(avnrun[2]),
                nr_running(), nr_threads,
                task_active_pid_ns(current)->last_pid);
        return 0;
}

Load的计算函数:

static unsigned long
calc_load(unsigned long load, unsigned long exp, unsigned long active)
{
        load *= exp;
        load += active * (FIXED_1 - exp);
        return load >> FSHIFT;
}

/*
 * calc_load - update the avenrun load estimates 10 ticks after the
 * CPUs have updated calc_load_tasks.
 */
void calc_global_load(void)
{
        unsigned long upd = calc_load_update + 10;
        long active;

        if (time_before(jiffies, upd))
                return;

        active = atomic_long_read(&calc_load_tasks);
        active = active > 0 ? active * FIXED_1 : 0;

        avenrun[0] = calc_load(avenrun[0], EXP_1, active);
        avenrun[1] = calc_load(avenrun[1], EXP_5, active);
        avenrun[2] = calc_load(avenrun[2], EXP_15, active);

        calc_load_update += LOAD_FREQ;
}
/*
 * These are the constant used to fake the fixed-point load-average
 * counting. Some notes:
 *  - 11 bit fractions expand to 22 bits by the multiplies: this gives
 *    a load-average precision of 10 bits integer + 11 bits fractional
 *  - if you want to count load-averages more often, you need more
 *    precision, or rounding will get you. With 2-second counting freq,
 *    the EXP_n values would be 1981, 2034 and 2043 if still using only
 *    11 bit fractions.
 */
extern unsigned long avenrun[];         /* Load averages */
extern void get_avenrun(unsigned long *loads, unsigned long offset, int shift);

#define FSHIFT          11              /* nr of bits of precision */
#define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
#define LOAD_FREQ       (5*HZ+1)        /* 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;

从这个函数中可以看到,内核计算load采用的是一种平滑移动的算法,Linux的系统负载指运行队列的平均长度,需要注意的是:可运行的进程是指处于运行队列的进程,不是指正在运行的进程。即进程的状态是TASK_RUNNING或者TASK_UNINTERRUPTIBLE。

Linux内核定义一个长度为3的双字数组avenrun,双字的低11位用于存放负载的小数部分,高21位用于存放整数部分。当进程所耗的 CPU时间片数超过CPU在5秒内能够提供的时间片数时,内核计算上述的三个负载,负载初始化为0。

假设最近1、5、15分钟内的平均负载分别为 load1、load5和load15,那么下一个计算时刻到来时,内核通过下面的算式计算负载:
load1 -= load1 - exp(-5 / 60) -+ n (1 - exp(-5 / 60 ))
load5 -= load5 - exp(-5 / 300) + n (1 - exp(-5 / 300))
load15 = load15 exp(-5 / 900) + n (1 - exp(-5 / 900))
其中,exp(x)为e的x次幂,n为当前运行队列的长度。

如何找出系统中load高时处于运行队列的进程

通过前面的讲解,我们已经明白有可能系统有很高的负载但是CPU使用率却很低,或者负载很低而CPU利用率很高,这两者没有直接关系,如何用脚本统计出来处于运行队列的进程呢?

每隔1s统计一次:

#!/bin/bash
LANG=C
PATH=/sbin:/usr/sbin:/bin:/usr/bin
interval=1
length=86400
for i in $(seq 1 $(expr ${length} / ${interval}));do
date
LANG=C ps -eTo stat,pid,tid,ppid,comm --no-header | sed -e 's/^ \*//' | perl -nE 'chomp;say if (m!^\S*[RD]+\S*!)'
date
cat /proc/loadavg
echo -e "\n"
sleep ${interval}
done

从统计出来的结果可以看到:

at Jan 20 15:54:12 CST 2018
D      958   958   957 nginx
D      959   959   957 nginx
D      960   960   957 nginx
D      961   961   957 nginx
R      962   962   957 nginx
D      963   963   957 nginx
D      964   964   957 nginx
D      965   965   957 nginx
D      966   966   957 nginx
D      967   967   957 nginx
D      968   968   957 nginx
D      969   969   957 nginx
D      970   970   957 nginx
D      971   971   957 nginx
D      972   972   957 nginx
D      973   973   957 nginx
D      974   974   957 nginx
R      975   975   957 nginx
D      976   976   957 nginx
D      977   977   957 nginx
D      978   978   957 nginx
D      979   979   957 nginx
R      980   980   957 nginx
D      983   983   957 nginx
D      984   984   957 nginx
D      985   985   957 nginx
D      986   986   957 nginx
D      987   987   957 nginx
D      988   988   957 nginx
D      989   989   957 nginx
R    11908 11908 18870 ps
Sat Jan 20 15:54:12 CST 2018
25.76 20.60 19.00 12/404 11912
注:R代表运行中的队列,D是不可中断的睡眠进程

在load比较高的时候,有大量的nginx处于R或者D状态,他们才是造成load上升的元凶,和我们底层的负载确实是没有关系的。

最后也给大家share一下查CPU使用率比较高的线程小脚本:

#!/bin/bash
LANG=C
PATH=/sbin:/usr/sbin:/bin:/usr/bin
interval=1
length=86400
for i in $(seq 1 $(expr ${length} / ${interval}));do
date
LANG=C ps -eT -o%cpu,pid,tid,ppid,comm | grep -v CPU | sort -n -r | head -20
date
LANG=C cat /proc/loadavg
{ LANG=C ps -eT -o%cpu,pid,tid,ppid,comm | sed -e 's/^ *//' | tr -s ' ' | grep -v CPU | sort -n -r | cut -d ' ' -f 1 | xargs -I{} echo -n "{} + " && echo ' 0'; } | bc -l
sleep ${interval}
done
fuser -k $0

转载自:https://yq.aliyun.com/articles/484253?utm_content=m_42447

<think>好的,我需要回答用户如何在Linux中查看系统的总内存大小。首先,我得回忆一下常见的Linux命令。常用的方法有使用free命令,这个命令可以显示内存使用情况,包括总内存。然后是top和htop命令,这些工具也能显示内存信息。另外,/proc/meminfo文件也是一个信息来源,里面包含了详细的内存数据。可能还有dmidecode命令,不过这个可能需要root权限。 接下来,我需要确定每个方法的具体步骤和命令参数。例如,free命令通常用free -h来以人类可读的格式显示,这样用户更容易理解。对于/proc/meminfo,需要指导用户如何查看该文件并找到MemTotal这一行。top命令在启动后可能需要用户退出,可能需要说明如何查看和退出。dmidecode命令可能不太常用,但可以作为一个备选方案,特别是当其他方法不可用时。 还需要考虑用户可能没有安装某些工具,比如htop,这时候可能需要提示安装方法,或者建议使用其他更普遍可用的命令。同时,权限问题也要注意,比如dmidecode可能需要sudo权限,需要提醒用户。 另外,用户是在环境中,可能需要确认这些命令在中的有效性。不过一般来说,这些方法在中和物理中是一样的,因为Linux内核处理内存信息的方式相同。不过如果有特殊情况,比如某些工具提供了特定的命令,也可以提及,但可能用户更倾向于使用标准方法。 现在要组织回答结构,按照常用性排序。首先介绍free命令,然后top,接着/proc/meminfo,再其他方法。每个方法给出具体的命令示例,并解释输出中的关键部分,比如在free -h中,Total列显示总内存。还要注意单位转换,确保用户明白输出的数值单位,比如GiB和GB的区别,但可能不需要深入,只要指出总内存的位置即可。 最后,生成相关问题,考虑用户可能想进一步了解内存使用情况,如何监控内存,或者遇到内存不足时的处理方法。所以相关问题可以包括查看内存使用情况、监控内存的命令,以及处理内存不足的问题等。</think>### 查看Linux总内存的方法 在Linux中查看系统总内存大小,可通过以下常用方法实现: --- #### 方法1:使用 `free` 命令 ```bash free -h --giga ``` - **输出示例**: ``` total used free Mem: 3.7G 1.2G 2.5G Swap: 2.0G 0B 2.0G ``` - **说明**:`total`列显示物理内存总大小(此处示例为3.7GB)。`-h`参数表示以人类可读格式(GiB/GB)显示,`--giga`强制以GB为单位[^1]。 --- #### 方法2:通过 `/proc/meminfo` 文件 ```bash cat /proc/meminfo | grep MemTotal ``` - **输出示例**: ``` MemTotal: 3880444 kB ``` - **说明**:直接读取内核内存信息文件,`MemTotal`表示总内存(单位为KB)。1GB≈1,048,576KB,可自行换算[^2]。 --- #### 方法3:使用 `top` 命令 1. 运行 `top` 2. 查看首行信息: ``` KiB Mem : 3880444 total, 2500000 free, ... ``` - **说明**:首行`total`列显示总内存(单位为KB),按`q`退出。 --- #### 其他方法 - **`htop`(需安装)**:图形化显示内存信息。 - **`dmidecode`(需root权限)**: ```bash sudo dmidecode -t memory | grep Size ``` 可查看物理内存条详细信息。 --- ### 总结建议 - **推荐方法1**:`free -h`简单直观。 - **需精确值**:使用`/proc/meminfo`。 - **权限允许**:`dmidecode`可获取硬件级信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值