[OS] 再探Kernel Threads -2

内核线程(Kernel Thread)创建和执行机制:

在 Linux 内核中,内核线程(kthread) 是一种特殊类型的线程,专门用于执行内核任务。与用户态的进程和线程不同,内核线程不具有用户空间,它仅在内核空间运行。内核线程的创建、启动和管理都通过内核提供的接口来完成,最常用的是 kthread_create()wake_up_process()

1. 内核线程创建 (kthread_create):
struct task_struct *kthread_create(
    int (*threadfn)(void *data),
    void *data,
    const char *namefmt, ...);

 

  • 参数解释:

    • int (*threadfn)(void *data):
      • 这是线程的主函数指针,它是一个接收 void *data 参数的函数。在创建的线程开始执行时,内核会调用这个函数。
      • 该函数通常是一个包含线程执行逻辑的循环,例如处理任务或响应事件。
    • void *data:
      • 传递给 threadfn 函数的数据,作为线程的执行上下文或参数。
    • const char *namefmt, ...:
      • 这是可变参数列表,通常用于设置线程的名称,便于调试和监控。
  • 返回值:

    • 返回一个指向内核线程对应的 task_struct 的指针。如果线程创建成功,该 task_struct 用于表示新创建的内核线程。
    • 该线程不会立即开始执行,必须通过调用 wake_up_process() 启动线程。
2. 线程启动 (wake_up_process):
int wake_up_process(struct task_struct *p);
  • kthread_create() 创建的线程不会自动开始执行。要使线程开始运行,需要将创建返回的 task_struct 指针传递给 wake_up_process() 函数,显式唤醒线程。
  • 这相当于告诉内核线程调度器,该线程已经准备好,可以开始调度并执行。

3. 内核线程执行函数 (thread_function):
int thread_function(void *data);
  • thread_function 是由 kthread_create() 创建的内核线程的主函数。它接收一个 void *data 参数,该参数是在线程创建时传入的上下文或数据。
  • 通常,thread_function 包含一个循环,用于处理任务或等待某些条件满足。它可以通过多种方式退出,如直接调用 do_exit() 或者返回 kthread_should_stop()true

4. 内核线程停止和退出:
  1. 直接退出:
    do_exit(0);
    

  2. 如果线程是独立运行且不需要被显式停止(例如不需要外部的 kthread_stop() 调用来结束它),线程可以在完成其任务后直接调用 do_exit() 函数。这会立即终止线程的执行,并释放相关资源。
  3. 通过 kthread_should_stop() 退出:
    if (kthread_should_stop())
        return 0;
    

  4. 内核提供了 kthread_should_stop() 函数,用于检测线程是否应该停止。当其他进程调用 kthread_stop() 时,该函数会返回 true
  5. 线程在循环中检查 kthread_should_stop() 的返回值,如果为 true,则线程可以选择退出。这通常用于线程需要被外部显式停止的情况。

完整内核线程示例:

#include <linux/kthread.h>  // 内核线程 API
#include <linux/delay.h>    // 休眠函数
#include <linux/sched.h>    // 调度 API
#include <linux/module.h>   // 模块定义
#include <linux/kernel.h>   // 内核函数

// 线程函数
int thread_function(void *data) {
    while (!kthread_should_stop()) {
        pr_info("Kernel thread running\n");
        ssleep(5);  // 线程休眠 5 秒
    }
    pr_info("Kernel thread stopping\n");
    return 0;  // 返回退出
}

static struct task_struct *task;

// 模块初始化函数
static int __init my_module_init(void) {
    // 创建内核线程
    task = kthread_create(thread_function, NULL, "my_kthread");
    
    if (IS_ERR(task)) {
        pr_err("Failed to create kernel thread\n");
        return PTR_ERR(task);
    }

    // 启动线程
    wake_up_process(task);
    pr_info("Kernel thread created successfully\n");
    return 0;
}

// 模块退出函数
static void __exit my_module_exit(void) {
    // 停止线程
    if (task) {
        kthread_stop(task);
        pr_info("Kernel thread stopped\n");
    }
}

module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");

你提到的几个函数是与内核线程相关的重要 API,用于创建、启动和管理内核线程。下面我将详细说明每个函数的返回值和使用场景,并解释 kthread_run() 函数的便捷性。

1. kthread_create() 返回值

struct task_struct *kthread_create(
    int (*threadfn)(void *data),
    void *data,
    const char *namefmt, ...);
  • 成功:
    • kthread_create() 成功执行时,它会返回一个指向新创建的内核线程的 task_struct 结构体指针。这个 task_struct 包含了内核线程的所有信息,类似于进程的描述符。
  • 失败:
    • 如果 kthread_create() 执行失败,它不会返回一个普通的指针,而是返回一个通过 ERR_PTR 宏包装的错误码。你可以使用 IS_ERR()PTR_ERR() 来检查和解析这个错误码。
    • ERR_PTR:这是内核用于将错误码转换为指针的机制,避免直接返回 NULL 指针。
  • 示例:
    struct task_struct *task;
    
    task = kthread_create(thread_function, NULL, "my_kthread");
    
    if (IS_ERR(task)) {
        pr_err("Thread creation failed: %ld\n", PTR_ERR(task));
        return PTR_ERR(task); // 返回错误码
    }
    

2. 启动内核线程:wake_up_process() 

int wake_up_process(struct task_struct *p);
  • 内核线程在创建时并不会立即运行。必须通过调用 wake_up_process() 函数将线程加入调度队列,通知内核调度器开始调度该线程。

  • 参数:

    • struct task_struct *p:指向内核线程的 task_struct 结构体。
  • 返回值:

    • 返回 1,表示成功唤醒线程并加入调度队列。
    • 返回 0,表示线程已经处于活动状态,不需要再次唤醒。
  • 示例:

    if (task) {
        wake_up_process(task);
    }
    

3. kthread_run():创建并启动内核线程的便捷函数 

struct task_struct *kthread_run(
    int (*threadfn)(void *data),
    void *data,
    const char *namefmt, ...);
  • kthread_run()kthread_create()wake_up_process() 的便捷组合。它在内核中同时创建和启动一个新线程。你无需显式调用 wake_up_process(),因为该函数会自动调用它。

  • 参数:

    • kthread_create() 相同:
      • int (*threadfn)(void *data):指向线程主函数的指针。
      • void *data:传递给线程函数的参数。
      • const char *namefmt:线程的名称。
  • 返回值:

    • 成功: 返回一个指向新线程的 task_struct 指针,表示线程已经创建并启动。
    • 失败: 返回一个 ERR_PTR,可以使用 IS_ERR() 来检查。
  • 示例:

    struct task_struct *task;
    
    // 创建并启动内核线程
    task = kthread_run(thread_function, NULL, "my_kthread");
    
    if (IS_ERR(task)) {
        pr_err("Failed to create and run kernel thread: %ld\n", PTR_ERR(task));
        return PTR_ERR(task); // 返回错误码
    }
    

总结:

  • kthread_create():用于创建一个新的内核线程,但不会立即启动,必须使用 wake_up_process() 来唤醒线程。

    • 返回值:成功时返回 task_struct *,失败时返回 ERR_PTR
  • wake_up_process():用于启动由 kthread_create() 创建的线程,将其加入调度队列。

    • 返回值:1 表示成功唤醒线程,0 表示线程已经在运行。
  • kthread_run():这是一个便捷函数,结合了 kthread_create()wake_up_process() 的功能,创建并启动线程。

    • 返回值:成功时返回 task_struct *,失败时返回 ERR_PTR

通过使用 kthread_run(),可以简化内核线程的创建和启动过程,避免显式调用 wake_up_process()

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>

MODULE_LICENSE("GPL");

static struct task_struct *task;

// Implement test function
int func(void *data) {
    int time_count = 0;
    do {
        printk(KERN_INFO "thread_function: %d times\n", ++time_count);
    } while (!kthread_should_stop() && time_count < 30);
    
    return time_count;
}

static int __init KT_init(void) {
    printk("KT module create kthread start\n");
    
    // Create a kthread
    task = kthread_create(&func, NULL, "MyThread");
    
    // Wake up new thread if ok
    if (!IS_ERR(task)) {
        printk("kthread starts\n");
        wake_up_process(task);
    }
    return 0;
}

static void __exit KT_exit(void) {
    printk("KT module exits! \n");
}

module_init(KT_init);
module_exit(KT_exit);

代码解释:

  1. 模块和内核线程的初始化

    • 包含了必要的内核头文件,如 init.hmodule.hkthread.h,这些头文件提供了内核模块和内核线程相关的函数。
    • MODULE_LICENSE("GPL"); 用于指定模块的许可证,这里是 GPL。
  2. func() 函数

    • 这是内核线程的主执行函数,函数 func(void *data) 将被线程在执行时调用。
    • 功能
      • time_count 是一个计数器,初始值为 0。
      • do...while 循环中,线程每次循环增加计数并使用 printk() 输出当前的计数值。
      • 循环条件是 !kthread_should_stop() 并且 time_count < 30kthread_should_stop() 用于检测线程是否被请求停止。
      • 当计数达到 30 或 kthread_should_stop() 返回 true 时,循环结束,函数返回计数器的最终值。
  3. KT_init() 函数

    • 这是模块初始化函数,在模块加载时执行。
    • 功能
      • printk("KT module create kthread start\n"); 用于输出信息,表示模块正在创建一个内核线程。
      • task = kthread_create(&func, NULL, "MyThread"); 创建一个内核线程,线程主函数为 func(),线程名称为 "MyThread"task 保存返回的 task_struct
      • 检查 task 是否有效,IS_ERR(task) 用于检查线程创建是否失败。如果成功,则调用 wake_up_process(task) 启动线程。
      • 线程启动后,内核线程开始执行 func() 中的逻辑。
  4. KT_exit() 函数

    • 这是模块卸载时执行的函数。
    • 功能
      • printk("KT module exits! \n"); 输出模块退出的提示。
      • 此函数并没有显式停止内核线程(可以在实际开发中通过 kthread_stop() 来显式停止线程)。
  5. 模块入口和退出函数

    • module_init(KT_init); 指定模块加载时调用 KT_init()
    • module_exit(KT_exit); 指定模块卸载时调用 KT_exit()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值