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:~$ uptime13:04:33 up 2 min, 2 users, load average: 1.96, 1.09, 0.44
当前时间、系统运行时间、当前用户数、1分钟平均负载、5分钟平均负载、15分钟平均负载
1.2.2cat /proc/loadavg
fengyuwuzu@fengyuwuzu:~$ cat /proc/loadavg0.07 0.02 0.00 1/424 7819
前三个数同上loadavg的值。第4个参数:分子是当前正在运行的进程数,分母是总的进程数。第5个参数:最近运行进程的ID。
1.2.3 w命令和top命令
fengyuwuzu@fengyuwuzu:~$ w11:22:14 up 22:20, 2 users, load average: 0.00, 0.00, 0.00USERTTYFROM LOGIN@ IDLE JCPU PCPU WHATfengyuwu tty7:0六13 22:20m 47.66s 0.20s /sbin/upstart --userfengyuwu pts/19 192.168.0.1六13 0.00s 0.12s 0.01s w
fengyuwuzu@fengyuwuzu:~$ toptop - 11:25:36 up 22:24, 2 users, load average: 0.00, 0.00, 0.00Tasks: 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 stKiB Mem : 1951332 total, 221652 free, 681956 used, 1047724 buff/cacheKiB Swap: 2094076 total, 2091504 free,2572 used. 1022792 avail Mem
2、Linux内核负载率分析(内核版本 4.1.15)
2.1 内核相关负载率计算
2.1.1kernel/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.2kernel/sched/proc.c
查看源码:/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 longcalc_load(unsigned long load, unsigned long exp, unsigned long active){load *= exp;load += active * (FIXED_1 - exp);load += 1UL << (FSHIFT - 1);return load >> FSHIFT;}