You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux自定义内核模块遍历进程子进程致系统崩溃求助

问题分析与修复方案

看起来你遇到的核心问题是循环变量被意外篡改导致的无限循环,同时代码里还有几个逻辑小问题,让我一步步给你拆解:

核心问题:循环变量被覆盖

在你的list_for_each遍历子进程的代码块里,你直接复用了外层for_each_process循环的task变量:

task = list_entry(list, struct task_struct, sibling);

for_each_process宏是依赖task变量的状态来遍历系统中所有进程的,一旦你在循环内部修改了这个变量,整个遍历逻辑就会彻底混乱——下一次迭代时,for_each_process会从你修改后的task(某个子进程)开始继续遍历,最终陷入无限循环,疯狂打印日志直到内核日志缓冲区被占满,甚至拖垮系统。

其他逻辑问题

  1. 错误的return位置:你的return 0;写在了for_each_process循环的内部,这意味着不管有没有找到目标PID,第一次进程遍历就会直接退出初始化函数,根本不会完成整个进程查找。
  2. 未校验输入参数:默认mypid=0,而PID 0是内核idle进程,处理它的子进程没有实际意义,应该先判断mypid是否合法。
  3. 过高的日志级别:用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);

关键修复点说明

  1. 新增独立的子进程变量:用child_task来存储遍历到的子进程,完全不干扰外层for_each_process循环的task变量,彻底避免循环逻辑混乱。
  2. 调整return位置:把return 0;移到for_each_process循环外面,确保进程遍历逻辑可以正常完成,找到目标PID后用break提前退出循环即可。
  3. 参数合法性校验:先判断mypid是否为正整数,非法输入直接返回错误码,避免无效的进程查找。
  4. 降低日志级别:把普通调试信息的打印级别改成KERN_INFO,只有警告/错误用KERN_WARNING,减少日志缓冲区的压力。

测试建议

  1. 编译修复后的模块,用sudo insmod listtasks.ko mypid=<合法PID>加载,比如可以用$$获取当前shell的PID:sudo insmod listtasks.ko mypid=$$。
  2. 用dmesg -w实时查看内核日志,确认不会出现无限打印的情况。
  3. 卸载模块用sudo rmmod listtasks,再用dmesg查看退出日志。

内容的提问来源于stack exchange,提问作者TrkDgnr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:47:34