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

卸载BeagleBone Black内核模块时kthread_stop()触发内存段错误排查

内核模块kthread_stop段错误问题分析与解决

问题根源

  • 你的线程函数thread_function执行完return 0后,内核会自动回收该线程对应的task_struct结构体,此时全局变量kthread变成野指针
  • 卸载模块时调用kthread_stop(kthread),实际是对已经被释放的内存地址进行操作,触发内核页错误(Oops)

从dmesg日志里的WARNING: CPU: 0 PID: 226 at kernel/kthread.c:73 to_kthread+0x24/0x30也能看出,内核检测到你传入kthread_stop的指针不是有效的kthread结构体指针。

修复方案

要让线程持续运行直到被kthread_stop终止,需要在线程函数里循环检查kthread_should_stop()标记,修改后的代码如下:

#include<linux/module.h>     // 内核模块基础头文件
#include<linux/init.h>       // __init和__exit宏定义
#include<linux/kernel.h>
#include<linux/kthread.h>    // 内核线程相关函数
#include<linux/sched.h>      // task_struct结构体定义
#include<linux/delay.h>      // 内核延迟函数

static struct task_struct *kthread;

/*
* 内核线程主函数,持续运行直到收到终止信号
*/
int thread_function(void *idx){
    pr_info("GPIO toggle thread running!\n");
    // 循环检查是否需要终止线程
    while (!kthread_should_stop()) {
        // 这里可以添加你的GPIO操作逻辑
        msleep(1000); // 模拟工作延迟
    }
    pr_info("GPIO toggle thread exiting!\n");
    return 0;
}

/*
* 模块加载初始化函数
*/
static int __init mod_init(void){
    pr_info("Initialising thread module\n");

    kthread = kthread_create(thread_function, NULL, "GPIO_Thread");
    if(kthread != NULL){
        wake_up_process(kthread);
        pr_info("%s is running\n", kthread->comm);
    }else{
        pr_info("kthread GPIO_Thread could not be created...\n");
        return -1;
    }
    pr_info("the thread is initialised and running!\n");
    return 0;
}

/*
* 模块卸载清理函数
*/
static void __exit mod_exit(void){
    pr_info("exiting thread module...\n");
    if (kthread) {
        int kstp = kthread_stop(kthread);
        pr_info("kthread exit code = %d", kstp);
        kthread = NULL; // 清空指针避免野指针
    }
    pr_info("thread module stopped!\n");
} 

module_init(mod_init);
module_exit(mod_exit);

MODULE_LICENSE("GPL");

关键修改点

  • 在thread_function中加入while (!kthread_should_stop())循环,让线程保持运行状态,直到kthread_stop被调用
  • 调用kthread_stop前先检查kthread指针是否有效,调用后清空指针,避免后续误操作
  • 把打印的固定字符串"th_name"改成kthread->comm,实际输出线程名称

补充说明

  • 内核线程创建后,如果线程函数直接返回,内核会立即销毁该线程的task_struct,此时保存的指针就失效了
  • kthread_stop的作用是给线程发送终止信号,并等待线程退出,只能对正在运行的内核线程调用
  • 模块卸载时必须确保所有资源都被正确回收,避免野指针、内存泄漏等问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 11:18:08