卸载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
相关产品推荐
相关产品推荐

