负载是查看Linux服务器运行状态时很常用的一个性能指标。|你真的了解linux中的负载吗?

负载是查看Linux服务器运行状态时很常用的一个性能指标 。 在观察线上服务器运行状况的时候 , 我们也是经常把负载找出来看一看 。 在线上请求压力过大的时候 , 经常是也伴随着负载的飙高 。
负载是查看Linux服务器运行状态时很常用的一个性能指标。|你真的了解linux中的负载吗?】但是负载的原理你真的理解了吗?我来列举几个问题 , 看看你对负载的理解是否足够的深刻 。
负载是如何计算出来的?
负载高低和CPU消耗正相关吗?
内核是如何暴露负载数据给应用层的?
如果你对以上问题的理解还拿捏不是很准 , 那么飞哥今天就带你来深入地了解一下Linux中的负载!一、理解负载查看过程
我们经常用top命令查看Linux系统的负载情况 。 一个典型的top命令输出的负载如下所示 。 #topLoadAvg:1.25,1.30,1.95...........
输出中的LoadAvg就是我们常说的负载 , 也叫系统平均负载 。 因为单纯某一个瞬时的负载值并没有太大意义 。 所以Linux是计算了过去一段时间内的平均值 , 这三个数分别代表的是过去1分钟、过去5分钟和过去15分钟的平均负载值 。
那么top命令展示的数据数是如何来的呢?事实上 , top命令里的负载值是从/proc/loadavg这个伪文件里来的 。 通过strace命令跟踪top命令的系统调用可以看的到这个过程 。 #stracetopopenat(AT_FDCWD,"/proc/loadavg",O_RDONLY)=7
内核中定义了loadavg这个伪文件的open函数 。 当用户态访问/proc/loadavg会触发内核定义的函数 , 在这里会读取内核中的平均负载变量 , 简单计算后便可展示出来 。 整体流程如下图所示 。
负载是查看Linux服务器运行状态时很常用的一个性能指标。|你真的了解linux中的负载吗?
文章图片
我们根据上述流程图再展开了看下 。 伪文件/proc/loadavg在kernel中定义是在/fs/proc/loadavg.c中 。 在该文件中会创建/proc/loadavg , 并为其指定操作方法loadavg_proc_fops 。 //file:fs/proc/loadavg.cstaticint__initproc_loadavg_init(void){proc_create("loadavg",0,NULL,&loadavg_proc_fops);return0;}
在loadavg_proc_fops中包含了打开该文件时对应的操作方法 。 //file:fs/proc/loadavg.cstaticconststructfile_operationsloadavg_proc_fops={.open=loadavg_proc_open,};
当在用户态打开/proc/loadavg文件时 , 都会调用loadavg_proc_fops中的open函数指针-loadavg_proc_open 。 loadavg_proc_open接下来会调用loadavg_proc_show进行处理 , 核心的计算是在这里完成的 。 //file:fs/proc/loadavg.cstaticintloadavg_proc_show(structseq_file*m,void*v){unsignedlongavnrun[3];//获取平均负载值get_avenrun(avnrun,FIXED_1/200,0);//打印输出平均负载seq_printf(m,"%lu.%02lu%lu.%02lu%lu.%02lu%ld/%d%dn",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);return0;}
在loadavg_proc_show函数中做了两件事 。
调用get_avenrun读取当前负载值
将平均负载值按照一定的格式打印输出
在上面的源码中 , 大家看到了FIXED_1/200、LOAD_INT、LOAD_FRAC等奇奇怪怪的定义 , 代码写的这么猥琐是因为内核中并没有float、double等浮点数类型 , 而是用整数来模拟的 。 这些代码都是为了在整数和小数之间转化使的 。 知道这个背景就行了 , 不用过度展开剖析 。
这样用户通过访问/proc/loadavg文件就可以读取到内核计算的负载数据了 。 其中获取get_avenrun只是在访问avenrun这个全局数组而已 。 //file:kernel/sched/core.cvoidget_avenrun(unsignedlong*loads,unsignedlongoffset,intshift){loads[0]=(avenrun[0]+offset)<shift;loads[1]=(avenrun[1]+offset)<shift;loads[2]=(avenrun[2]+offset)<shift;}
现在可以总结一下我们开篇中的一个问题:内核是如何暴露负载数据给应用层的?