(一篇解决~)利用可挂载内核模块进行高负载处理
概述
本程序在高负载处理模块的代码基础上,根据5.15版内核的变化,修改出的。本程序是一个内核模块,用于监控系统负载,在平均负载超过4时,打印所有进程的调用栈。
本程序分为三个文件:main.c、load.h、Makefile。其中,main.c是本内核模块的主程序;load.h中是该内核模块的扩展代码,这里放了一个获取内核中未被导出符号(变量或函数)的一个函数;Makefile用来编译该内核模块。完整代码在文章的最下面。
模块的主要实现方式为:设置一个定时器,以固定的间隔访问系统给出的1分钟内平均负载,如果超过负载阈值,则输出运行队列全部进程栈信息,并使程序休眠一段较长的时间。流程图如下:

定时器
本模块采用了hrtimer——高精度定时器,由linux/hrtimer.h引入,可精确到ns级。
平均负载
这里有所改动,原文中是通过kallsyms_lookup_name函数获取的,但我在浏览头文件时发现了linux/sched/loadavg.h头文件,里面已经定义好了一些有关平均负载——loadavg的宏,并导出了avenrun——平均负载数组——1、5、15分钟内的平均负载,所以我这里直接引用了该头文件、直接使用了相关符号
输出进程栈
这里改动很大,在5.15版中,没有了save_stack_trace_tsk,通过查看linux/stacktrace.h文件,发现这个函数被用于未配置CONFIG_ARCH_STACKWALK的系统,而配置了CONFIG_ARCH_STACKWALK的系统中,有新的函数:unsigned int stack_trace_save_tsk(struct task_struct *task, unsigned long *store, unsigned int size, unsigned int skipnr),定义于kernel/stacktrace.c中,与旧函数相比变化很大,好在在源代码中有详细的接口说明,根据这我成功的修改了栈的输出部分。
与此同时我发现了功能类似的另一个函数show_stack,定义于arch/x86/kernel/dumpstack.c
然而,这两个函数的符号都没有导出,也就无法通过引入相关头文件来使用,原文章来获取内核中未导出符号的kallsyms_lookup_name函数也未被导出,这就要求我寻找一种新的方法来获取未导出符号,我找到了kprobe技术。
kprobes技术^[3]^是内核开发者们专门为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术。利用kprobes技术,内核开发人员可以在内核的绝大多数指定函数中动态的插入探测点来收集所需的调试状态信息而基本不影响内核原有的执行流程。我们可以通过注册一个指定了函数名的kprobe来获取函数的地址。
【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)


main.c
load.h
Makefile
运行结果
将三个文件放入一个单独的文件夹中,运行make命令,编译出可插入内核的程序。编译好后,运行sudo insmod load_monitor.ko命令将其插入内核。
接下来是测试,运行stress -c 8命令(stress需要另外安装),使平均负载快速到达4以上,这里可以在新的虚拟终端通过top命令实时观测负载。当负载到达4之后,在运行着stress命令的窗口中按下ctrl+c终止程序,运行sudo dmesg命令就可以查看到内核栈的输出信息。

