0、引入
CPU负载率一定程度上反映了系统CPU的繁忙程度,且在多核cpu下与负载均衡,进程调度关系密切。实现对CPU负载率的监测有利于实现一套稳定可靠适用的系统。
1、负载率相关定义
1.1 平均负载的定义
1.1.1 Load average理解:
-
在特定时间间隔内运行队列中的平均进程数,内核一般取5、10、15分钟。
-
单位时间内,系统处于可运行状态和不可中断状态的平均进程数,即平均活跃进程数
1.1.2 可运行状态和不可中断状态的进程
-
可运行状态的进程:正在使用cpu或者正在等待cpu的进程,ps命令看到的,处于R状态(Running或Runnable)的进程
-
不可中断状态的进程:正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的I/O响应,也就是我们在ps命令中看到的D状态(UninterruptibleSleep,也称为Disk Sleep)的进程。不可中断状态实际上是系统对进程和硬件设备的一种保护机制。
1.1.3 运行队列
它没有在等待I/O操作的结果、它没有主动进入等待状态(也就是没有调用'wait')、没有被停止(例如:等待终止)则处于运行队列之中,即以上描述的可运行态和不可中断态的进程。
1.2 Linux下如何查看平均负载
1.2.1 uptime命令
fengyuwuzu@fengyuwuzu:~$ uptime
13:04:33 up 2 min, 2 users, load average: 1.96, 1.09, 0.44
当前时间、系统运行时间、当前用户数、1分钟平均负载、5分钟平均负载、15分钟平均负载
1.2.2 cat /proc/loadavg
fengyuwuzu@fengyuwuzu:~$ cat /proc/loadavg
0.07 0.02 0.00 1/424 7819
前三个数同上loadavg的值。第4个参数:分子是当前正在运行的进程数,分母是总的进程数。第5个参数:最近运行进程的ID。
1.2.3 w命令和top命令
fengyuwuzu@fengyuwuzu:~$ w
11:22:14 up 22:20, 2 users, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
fengyuwu tty7 :0 六13 22:20m 47.66s 0.20s /sbin/upstart --user
fengyuwu pts/19 192.168.0.1 六13 0.00s 0.12s 0.01s w
fengyuwuzu@fengyuwuzu:~$ top
top - 11:25:36 up 22:24, 2 users, load average: 0.00, 0.00, 0.00
Tasks: 238 total, 1 running, 169 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1951332 total, 221652 free, 681956 used, 1047724 buff/cache
KiB Swap: 2094076 total, 2091504 free, 2572 used. 1022792 avail Mem
2、Linux内核负载率分析(内核版本 4.1.15)
2.1 内核相关负载率计算
2.1.1 kernel/fs/proc/loadavg.c
实现对外的提供读取loadavg接口。
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/pid_namespace.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/seqlock.h>
#include <linux/time.h>
#define LOAD_INT(x) ((x) >> FSHIFT)
#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
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;
}
static int loadavg_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, loadavg_proc_show, NULL);
}
static const struct file_operations loadavg_proc_fops = {
.open = loadavg_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __init proc_loadavg_init(void)
{
proc_create("loadavg", 0, NULL, &loadavg_proc_fops);
return 0;
}
fs_initcall(proc_loadavg_init);
loadavg_proc_show调用get_avenrun获取最新的负载率数组。
2.1.2 kernel/sched/proc.c
查看源码:https://elixir.bootlin.com/linux/v4.1.15/source/kernel/sched/proc.c#L98
/* Variables and functions for calc_load */
atomic_long_t calc_load_tasks;
unsigned long calc_load_update;
unsigned long avenrun[3];
EXPORT_SYMBOL(avenrun); /* should be removed */
/**
* get_avenrun - get the load average array
* @loads: pointer to dest load array
* @offset: offset to add
* @shift: shift count to shift the result left
*
* These values are estimates at best, so no need for locking.
*/
void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
{
loads[0] = (avenrun[0] + offset) << shift;
loads[1] = (avenrun[1] + offset) << shift;
loads[2] = (avenrun[2] + offset) << shift;
}
* calc_load - update the avenrun load estimates 10 ticks after the
* CPUs have updated calc_load_tasks.
*/
void calc_global_load(unsigned long ticks)
{
long active, delta;
if (time_before(jiffies, calc_load_update + 10))
return;
/*
* Fold the 'old' idle-delta to include all NO_HZ cpus.
*/
delta = calc_load_fold_idle();
if (delta)
atomic_long_add(delta, &calc_load_tasks);
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;
/*
* In case we idled for multiple LOAD_FREQ intervals, catch up in bulk.
*/
calc_global_nohz();
}
/*
* a1 = a0 * e + a * (1 - e)
*/
static unsigned long
calc_load(unsigned long load, unsigned long exp, unsigned long active)
{
load *= exp;
load += active * (FIXED_1 - exp);
load += 1UL << (FSHIFT - 1);
return load >> FSHIFT;
}