c - 用 "return"或 "do_exit()"终止内核线程是一个好习惯吗?

标签 c linux multithreading linux-kernel linux-device-driver

我的目标是从我的驱动程序的探测函数执行一次内核线程,执行固件下载。

为简单起见,提供示例代码(不是实际代码),

#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");

}

我没有使用以下任何一项,因为:

  1. kthread_should_stop() - 用于我不想要的连续执行
  2. kthread_stop(struct task_struct *thread) - 如果包含在模块退出函数中,会导致内核 panic ,因为线程在执行一次后已经终止

这是正确的做法吗?如果不是请建议

最佳答案

您需要确保在模块退出函数返回之前线程已经消失。一种方法是使用“完成”结构。

基本思想是在启动线程之前初始化完成结构,让线程在退出时将完成结构标记为“完成”,让模块退出函数(或其他)等待完成结构被标记完成。

  1. 先决条件

    #include <linux/completion.h>
    
  2. 初始化完成结构

    如果完成结构变量是静态分配的,它可以在变量定义中使用DECLARE_COMPLETION宏初始化:

    static DECLARE_COMPLETION(thread_done);
    

    (还有一个 DECLARE_COMPLETION_ONSTACK 宏,用于当完成结构变量在堆栈上时使用。)

    或者,可以定义未初始化的struct completion(例如作为动态分配结构的成员)并随后通过调用init_completion(...) 进行初始化:

    struct completion thread_done;
    
    ...
    
    init_completion(&thread_done);
    
  3. 创建线程

    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(&thread_done); 调用。

  4. 退出话题

    与其从线程函数返回或调用 do_exit(...),线程应该调用 complete_and_exit(...) 以将线程标记为“完成” ":

    complete_and_exit(&thread_done, 0);
    

    调用 complete_and_exit(...) 比单独调用 complete(...)do_exit(...) 更安全.通过单独调用 complete(...)do_exit(...) ,当 complete( ...) 返回,因此线程可以执行不存在的或随机的代码。调用 complete_and_exit(...) 可以避免这种情况的发生,因为该函数存在于模块代码之外并且永远不会返回。

  5. 确保线程已完成

    为确保线程已完成,调用wait_for_completion(...):

    wait_for_completion(&thread_done);
    

    返回时,线程要么已经退出,要么在对 complete_and_exit(...) 的调用中仍在运行并且即将退出。无论哪种情况,它都不再运行任何模块代码,因此可以安全地继续。

关于c - 用 "return"或 "do_exit()"终止内核线程是一个好习惯吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56522428/

相关文章:

c - 动态数组是在没有 malloc 的情况下分配的

c - 为什么打印这个字符串时我做错了?

linux - 如何找出docker镜像中安装了哪个Linux?

php - 为什么我无法删除php文件?

c - Makefile "makefile"中的 "kaodv.o: $(KOBJS) Makefile"是什么意思

c++ - 分析多线程代码,采样如何工作

c# - C# 中类似 Java 的线程同步

c++ - 了解 MSVS C++ 编译器优化

c++ - 线程 ID 在 std::thread 和 tbb::task_group 之间重用导致 OpenMP 中的死锁

c++ - 在 Qt-Projekt 中加载 libavcodec