全面解析 Linux Load( 二 )


加载load5s的内核模块,即可获取更加灵敏的5秒钟load average(目前已经适配过centos7的3.10.0内核和centos6的2.6.32内核) 。具体如下:

全面解析 Linux Load

文章插图
安装完load5s.ko内核模块之后,即可在/proc/目录中获取load5s值:
多次cat这个伪文件可以发现,该值5秒内不会变化,每隔5秒会发生变化 。这就是比load1更加灵敏的load5s值 。也就是说,如果你安装了load5s模块,当你的load1高时,你可以看看load5s的值,如果它已经不高了,很可能说明你的系统已经开始逐步恢复中 。
load5s除了比load1更加灵敏这个用途之外,它也是能帮我们揭示load1、load5和load15的计算逻辑的最关键因素 。我们可以尝试在安装了load5s模块的机器上,运行如下load_predict.sh的shell脚本 。
(下图为节选,详情可参考GIT)
全面解析 Linux Load

文章插图
运行shell脚本后有如下输出:
全面解析 Linux Load

文章插图
从数据结果中我们可以看到,current_load列和predict_load列分别在load1、load5和load15三种情况下惊人的一致 。其中current_load列是从当前系统中采集出来的load值,而predict_load是我们通过shell脚本中运算预测的load1、load5和load15的值,大家不妨多运行几次load_predict.sh脚本进行验证(采集值和预测值最多只有0.01的误差) 。
此时,我们再回过头来仔细看这个load_predict.sh脚本,原来这就是linux load的计算过程 。
 
4 Linux内核中load相关代码分析了解了linux load的计算逻辑,我们再来对照一下内核代码,以内核3.10.0版本为例 。将shell代码和c代码对比着看看,相信大家很快就会理解内核中load相关的C语言代码 。
首先来看下/proc/loadavg伪文件对应的内核代码 。
全面解析 Linux Load

文章插图
LOAD_INT(avnrun[0]), LOAD_FRAC(avnrun[0])预编译之后是((avnrun[0]) >> 11), ((((avnrun[0]) & ((1<<11)-1)) * 100) >> 11) 。由于内核没有小数计算,这段代码本质上是实现了avnrun[0]除以2048且取2位小数的浮点运算 。
get_avenrun函数定义在kernel/sched/core.c页面 。(下图为节选,详情可参考GIT)
全面解析 Linux Load

文章插图
我们可以看到get_avenrun函数通过avenrun全局数组变量,返回上面的avnrun数组变量 。avenrun全局数组变量在calc_global_load函数中每隔5001毫秒,由calc_load函数将active值添加到原有的load值中,进而产生新的load值 。
其中load1、load5和load15的区别只是第二个exp参数传入给calc_load函数不同的值,依次为1884、2014和2037 。这里active值从calc_load_tasks全局结构体变量获取 。calc_load_tasks全局结构体变量在calc_load_account_active函数中设置,而整个值的最初来源是calc_load_fold_active函数 。
在函数calc_load_fold_active中,我们可以看到最终获取的是rq(run queue)队列中的this_rq->nr_running和this_rq->nr_uninterruptible两种状态的task数 。
熟悉cpu调度算法的同学知道,这里实际上是把每个cpu队列里的nr_running和nr_uninterruptible值都汇总到一起 。而nr_running和nr_uninterruptible正好对应于用户空间中的R和D两种状态的线程的数量 。
回头再看前面man page说明中的jobs概念显然是不对的,正确的应该是tasks概念(用户空间对应threads概念) 。
load2process输出结果中第一列之和、load5s和load1值之间的关系也一目了然 。load2process第一列之和是运行这个脚本的瞬时nr_running和nr_uninterruptible状态线程数之和 。load5s值是每5001毫秒对nr_running和nr_uninterruptible状态线程数之和的一个采样 。load1是对之前历史所有load5s采样值按某种算法的平均值 。
 
5 内核代码的反汇编搞懂了内核load相关代码之后,我们还是要回过头来再看看load5s的实现机制 。理解了load5s的实现,才能让我们更加相信脚本load_predict.sh中的load计算逻辑 。
而在此之前有必要先搞清楚内核反汇编的知识 。linux启动时加载的内核binary程序存储在/boot/目录中,名称以vmlinuz开头,再加版本号命名 。
全面解析 Linux Load

文章插图
可以看到vmlinux是bzImage自解压格式 。这种格式是无法直接进行反汇编的,首先需要进行解压缩 。bizip压缩包内容的开始头部可以通过“1f 8b 08”这个签名来查找 。
全面解析 Linux Load


推荐阅读