Linux自定义内核模块遍历进程子进程致系统崩溃求助
问题分析与修复方案
看起来你遇到的核心问题是循环变量被意外篡改导致的无限循环,同时代码里还有几个逻辑小问题,让我一步步给你拆解:
核心问题:循环变量被覆盖
在你的list_for_each遍历子进程的代码块里,你直接复用了外层for_each_process循环的task变量:
task = list_entry(list, struct task_struct, sibling);
for_each_process宏是依赖task变量的状态来遍历系统中所有进程的,一旦你在循环内部修改了这个变量,整个遍历逻辑就会彻底混乱——下一次迭代时,for_each_process会从你修改后的task(某个子进程)开始继续遍历,最终陷入无限循环,疯狂打印日志直到内核日志缓冲区被占满,甚至拖垮系统。
其他逻辑问题
- 错误的
return位置:你的return 0;写在了for_each_process循环的内部,这意味着不管有没有找到目标PID,第一次进程遍历就会直接退出初始化函数,根本不会完成整个进程查找。 - 未校验输入参数:默认
mypid=0,而PID 0是内核idle进程,处理它的子进程没有实际意义,应该先判断mypid是否合法。 - 过高的日志级别:用
KERN_ALERT打印大量普通调试信息,会挤占高优先级日志的空间,也更容易导致日志缓冲区溢出。
修复后的代码
#include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/types.h> #include <linux/slab.h> #include <linux/moduleparam.h> #include <linux/sched/signal.h> MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Some guy"); int mypid = 0; module_param(mypid, int, 0); static int simple_init(void) { struct task_struct *task; struct task_struct *child_task; // 新增独立的子进程变量,避免覆盖外层循环变量 struct list_head *list; // 先校验输入的PID是否合法 if (mypid <= 0) { printk(KERN_WARNING "Invalid PID: %d. Please provide a positive integer.\n", mypid); return -EINVAL; } printk(KERN_INFO "Loading Module\nTarget PID: %d\n", mypid); for_each_process(task) { // 用KERN_INFO打印普通调试信息,避免占用高优先级日志 printk(KERN_INFO "Scanning PID/NAME: %d/%s\n", task->pid, task->comm); if (task->pid == mypid) { printk(KERN_INFO "Found target process: %d/%s\n", task->pid, task->comm); // 使用独立的child_task变量遍历子进程,不修改外层的task list_for_each(list, &task->children) { child_task = list_entry(list, struct task_struct, sibling); printk(KERN_INFO "Child PID/NAME: %d/%s\n", child_task->pid, child_task->comm); } // 找到目标进程后,可以提前退出循环,避免继续遍历其他进程 break; } } // return放在循环外面,确保整个初始化逻辑完成 return 0; } static void simple_exit(void) { printk(KERN_INFO "Removing Module\n"); } module_init(simple_init); module_exit(simple_exit);
关键修复点说明
- 新增独立的子进程变量:用
child_task来存储遍历到的子进程,完全不干扰外层for_each_process循环的task变量,彻底避免循环逻辑混乱。 - 调整
return位置:把return 0;移到for_each_process循环外面,确保进程遍历逻辑可以正常完成,找到目标PID后用break提前退出循环即可。 - 参数合法性校验:先判断
mypid是否为正整数,非法输入直接返回错误码,避免无效的进程查找。 - 降低日志级别:把普通调试信息的打印级别改成
KERN_INFO,只有警告/错误用KERN_WARNING,减少日志缓冲区的压力。
测试建议
- 编译修复后的模块,用
sudo insmod listtasks.ko mypid=<合法PID>加载,比如可以用$$获取当前shell的PID:sudo insmod listtasks.ko mypid=$$。 - 用
dmesg -w实时查看内核日志,确认不会出现无限打印的情况。 - 卸载模块用
sudo rmmod listtasks,再用dmesg查看退出日志。
内容的提问来源于stack exchange,提问作者TrkDgnr
相关产品推荐
相关产品推荐

