使用 "return" 或 "do_exit()" 终止内核线程是一种好习惯吗?
Is it a good practice to terminate kernel thread with "return" or "do_exit()"?
我的objective是从我的驱动程序的探测函数执行内核线程一次,执行固件下载。
为简单起见,提供示例代码(不是实际代码),
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kthread.h>
MODULE_LICENSE("GPL");
struct task_struct *kthread;
static int thread_func(void* data)
{
printk("In %s function\n", __func__);
return 0;
}
static int hello_init(void)
{
int ret = 0;
printk("Hello World\n");
kthread = kthread_run(thread_func,
NULL, "kthread-test");
if (IS_ERR(kthread)) {
ret = PTR_ERR(kthread);
printk("Unable to run kthread err %d\n", ret);
return ret;
}
return 0;
}
static void hello_exit(void)
{
printk("Bye World\n");
}
我没有使用以下任何一项,因为:
kthread_should_stop()
- 用于我不想要的连续执行
kthread_stop(struct task_struct *thread)
- 如果包含在模块退出函数中,会导致内核恐慌,因为线程在执行一次后已经终止
这是正确的做法吗?如果不是请建议
您需要确保线程在模块退出函数 returns 之前已经消失。一种方法是使用 "completion" 结构。
基本思路是在启动线程之前初始化完成结构,让线程在退出时将完成结构标记为"complete",让模块退出函数(或其他)等待完成要标记为完整的结构。
先决条件
#include <linux/completion.h>
初始化完成结构
如果完成结构变量是静态分配的,可以在变量定义中使用DECLARE_COMPLETION
宏初始化:
static DECLARE_COMPLETION(thread_done);
(还有一个DECLARE_COMPLETION_ONSTACK
宏,供完成结构变量入栈时使用。)
或者,可以定义未初始化的 struct completion
(例如,作为动态分配结构的成员)并随后通过调用 init_completion(...)
:
进行初始化
struct completion thread_done;
...
init_completion(&thread_done);
创建线程
kthread = kthread_run(thread_func, NULL, "kthread-test");
if (IS_ERR(kthread)) {
complete(&thread_done); /* <-- may or may not be required */
ret = PTR_ERR(kthread);
return ret;
}
在上面,如果kthread_run(...)
失败,完成结构被标记为"complete"以防某些代码等待稍后完成。如果保证后面什么都不等待完成,可以省略complete(&thread_done);
调用。
退出线程
而不是 return 从线程函数调用或调用 do_exit(...)
,线程应该调用 complete_and_exit(...)
以将线程标记为 "complete":
complete_and_exit(&thread_done, 0);
调用 complete_and_exit(...)
比单独调用 complete(...)
和 do_exit(...)
更安全。通过单独调用 complete(...)
和 do_exit(...)
,模块代码有可能在 complete(...)
return 时已经卸载,因此线程可以执行 non-existent 或随机代码。调用 complete_and_exit(...)
避免了这种情况的发生,因为该函数存在于模块代码之外并且永远不会 returns.
确保线程已完成
为确保线程已完成,请调用 wait_for_completion(...)
:
wait_for_completion(&thread_done);
在 return 上,线程要么已经退出,要么仍在 运行 调用 complete_and_exit(...)
中并且即将退出。无论哪种情况,它都不再是 运行 任何模块代码,因此可以安全地继续。
我的objective是从我的驱动程序的探测函数执行内核线程一次,执行固件下载。
为简单起见,提供示例代码(不是实际代码),
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kthread.h>
MODULE_LICENSE("GPL");
struct task_struct *kthread;
static int thread_func(void* data)
{
printk("In %s function\n", __func__);
return 0;
}
static int hello_init(void)
{
int ret = 0;
printk("Hello World\n");
kthread = kthread_run(thread_func,
NULL, "kthread-test");
if (IS_ERR(kthread)) {
ret = PTR_ERR(kthread);
printk("Unable to run kthread err %d\n", ret);
return ret;
}
return 0;
}
static void hello_exit(void)
{
printk("Bye World\n");
}
我没有使用以下任何一项,因为:
kthread_should_stop()
- 用于我不想要的连续执行kthread_stop(struct task_struct *thread)
- 如果包含在模块退出函数中,会导致内核恐慌,因为线程在执行一次后已经终止
这是正确的做法吗?如果不是请建议
您需要确保线程在模块退出函数 returns 之前已经消失。一种方法是使用 "completion" 结构。
基本思路是在启动线程之前初始化完成结构,让线程在退出时将完成结构标记为"complete",让模块退出函数(或其他)等待完成要标记为完整的结构。
先决条件
#include <linux/completion.h>
初始化完成结构
如果完成结构变量是静态分配的,可以在变量定义中使用
DECLARE_COMPLETION
宏初始化:static DECLARE_COMPLETION(thread_done);
(还有一个
DECLARE_COMPLETION_ONSTACK
宏,供完成结构变量入栈时使用。)或者,可以定义未初始化的
进行初始化struct completion
(例如,作为动态分配结构的成员)并随后通过调用init_completion(...)
:struct completion thread_done; ... init_completion(&thread_done);
创建线程
kthread = kthread_run(thread_func, NULL, "kthread-test"); if (IS_ERR(kthread)) { complete(&thread_done); /* <-- may or may not be required */ ret = PTR_ERR(kthread); return ret; }
在上面,如果
kthread_run(...)
失败,完成结构被标记为"complete"以防某些代码等待稍后完成。如果保证后面什么都不等待完成,可以省略complete(&thread_done);
调用。退出线程
而不是 return 从线程函数调用或调用
do_exit(...)
,线程应该调用complete_and_exit(...)
以将线程标记为 "complete":complete_and_exit(&thread_done, 0);
调用
complete_and_exit(...)
比单独调用complete(...)
和do_exit(...)
更安全。通过单独调用complete(...)
和do_exit(...)
,模块代码有可能在complete(...)
return 时已经卸载,因此线程可以执行 non-existent 或随机代码。调用complete_and_exit(...)
避免了这种情况的发生,因为该函数存在于模块代码之外并且永远不会 returns.确保线程已完成
为确保线程已完成,请调用
wait_for_completion(...)
:wait_for_completion(&thread_done);
在 return 上,线程要么已经退出,要么仍在 运行 调用
complete_and_exit(...)
中并且即将退出。无论哪种情况,它都不再是 运行 任何模块代码,因此可以安全地继续。