[OS] 再探Kernel Threads -2

news2024/11/24 7:02:41

内核线程(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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2195717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机的错误计算(一百一十六)

摘要 计算机的错误计算&#xff08;一百一十&#xff09;分析了&#xff08;二&#xff09;中例1循环迭代错误计算的原因。应读者建议&#xff0c;本节将用错数讨论其例2的错误计算原因。 例1. 已知 计算 在 的错数&#xff0c;并用实例分析计算过程中的错误数字数量。…

leetcode-哈希篇1

leetcode-217 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 &#xff0c;返回 true &#xff1b;如果数组中每个元素互不相同&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1] 输出&#xff1a;true 解释&#xff1a; 元素 1 在下标…

电脑操作技巧:如何恢复永久删除的婚礼照片

我们的生活充满了褪色和模糊的快照&#xff0c;是我们记忆的剪贴簿。尽管我们很想记住事情并坚持快乐的回忆&#xff0c;但随着时间的推移&#xff0c;它们会被冲走。为了避免这种情况并记住这些记忆&#xff0c;我们以照片的形式捕捉瞬间。这有助于缓解和分享那些快乐的时刻。…

C语言编译与链接(29)

文章目录 前言一、翻译环境和执行环境二、翻译环境预处理(预编译)编译词法分析语法分析语义分析 汇编链接 三、运行环境总结 前言 本篇同样是一篇修炼内功的文章   很重要&#xff01;它将会让你对程序的构建运行有一个更深的认识 一、翻译环境和执行环境 在ANSI C的任何一种…

[uni-app]小兔鲜-08云开发

uniCloud可以通过JS开发服务端,包含云数据库, 云函数, 云存储等功能, uniCloud可结合 uni-ui 组件库使用 效果展示: <picker>城市选择组件不支持h5端和APP端, 所以我们使用 <uni-data-picker>组件进行兼容处理 <uni-data-picker>的数据使用云数据库的数据 云…

指针(7)

目录 1. sizeof和strlen的对⽐ 1.1 sizeof 1.2 strlen sizeof 和 strlen 总结&#xff1a; 2. 数组和指针 2.1 ⼀维数组 2.2 字符数组 1. sizeof和strlen的对⽐ 1.1 sizeof 计算的是使⽤类型创建的变量所占内存空间的⼤⼩。sizeof不在乎你里面放的什么。sizieof是操作符…

基于springboot人力资源管理系统源码

项目技术&#xff1a;SpringBoot 运行环境&#xff1a;jdk1.8idea/eclipsemaven3mysql5.6 项目描述&#xff1a; 系统包括&#xff0c;员工管理&#xff0c;奖惩管理&#xff0c;合同管理&#xff0c;薪酬管理&#xff0c;培训管理&#xff0c;绩效评估等功能

x++、++x的一些问题

x、x在字面上无非就说一个先前置递增然后再运算&#xff0c;另一个是运算完再递增&#xff0c;是不是有些许模棱两可的感觉&#xff0c;接下来引用一个简单的for循环就能够大致理解&#xff1a; 先是x&#xff1a; int i0,x0;for(i0;(i)<5;){xi;printf("%d\n",x)…

影刀RPA实战:Excel排序、替换与格式

1.实战目标 今天继续介绍影刀RPA操作Excel的指令&#xff0c;内容替换&#xff0c;数据排序与单元格格式设置&#xff0c;这几个功能在日常工作中使用率还是比较频繁的。我们可以使用影刀来处理这些重复繁琐的工作。 2.内容替换 我们手动替换内容时 打开Excel文件&#xff1…

【Spring】“请求“ 之后端传参重命名,传递数组、集合,@PathVariable,@RequestPart

1. 后端传参重命名&#xff08;后端参数映射&#xff09; 某些特殊情况下&#xff0c;前端传递的参数 key 和我们后端接收的 key 可以不一致&#xff0c;比如前端传了一个 time 给后端&#xff0c;而后端是使用 createtime 字段来接收的&#xff0c;这样就会出现参数接收不到的…

第二十二天|回溯算法| 理论基础,77. 组合(剪枝),216. 组合总和III,17. 电话号码的字母组合

回溯算法理论基础 1.题目分类 2.理论基础 什么是回溯算法 回溯和递归是相辅相成的。 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯法的效率 回溯法其实就是暴力查找&#xff0c;并不是什么高效的算法。 因为回溯的本质是穷举&#xff0c;穷举所有可…

销冠的至高艺术:让自己不像销售

若想在销售领域脱颖而出&#xff0c;首先是让自己超越传统销售的框架&#xff0c;成为客户心中不可多得的行业顾问与信赖源泉。这不仅是身份的蜕变&#xff0c;更是影响力与信任度质的飞跃。 销冠对客户只吸引不骚扰&#xff0c;不讲自己卖什么&#xff0c;只讲自己能解决什么…

销售秘籍:故事+观点+结论

在销售的浩瀚宇宙中&#xff0c;隐藏着一个不朽的秘诀——利用人类共有的“错失恐惧”&#xff0c;激发客户内心的渴望与行动。正如村上春树所言&#xff0c;每个故事都深深植根于灵魂&#xff0c;而大仲马则揭示&#xff0c;灵魂之眼所见&#xff0c;比肉眼更为长久铭记。 错…

【C++】入门基础介绍(下)输入输出,函数重载,缺省与引用

文章目录 7. C输入与输出8. 缺省参数9. 函数重载10. 引用10. 1 引用的概念10. 2 引用的特性10. 3 引用的使用10. 4 const引用10. 5 指针和引用的关系 11. inline12. nullptr 7. C输入与输出 iostream是 Input Output Stream 的缩写&#xff0c;是标准输入、输出流库&#xff0…

k8s 中存储之 PV 持久卷 与 PVC 持久卷申请

目录 1 PV 与 PVC 介绍 1.1 PersistentVolume&#xff08;持久卷&#xff0c;简称PV&#xff09; 1.2 PersistentVolumeClaim&#xff08;持久卷声明&#xff0c;简称PVC&#xff09; 1.3 使用了PV和PVC之后&#xff0c;工作可以得到进一步的细分&#xff1a; 2 持久卷实验配置…

什么是安全运营中心 SOC?

SOC 代表安全运营中心&#xff0c;它是任何企业中负责组织安全、保护企业免受网络风险的单一、集中的团队或职能。 安全运营中心将管理和控制业务运营的所有安全要素&#xff0c;从监控资产到雇用合适的人员和流程&#xff0c;再到检测和应对威胁。 在本文中&#xff0c;我们…

sqli-labs less-14post报错注入updatexml

post提交报错注入 闭合方式及注入点 利用hackbar进行注入&#xff0c;构造post语句 unameaaa"passwdbbb&SubmitSubmit 页面报错&#xff0c;根据分析&#xff0c;闭合方式". 确定列数 构造 unameaaa" or 11 # &passwdbbb&SubmitSubmit 确定存在注…

【Blender Python】7.一些运算、三角函数以及随机

概述 要用Blender进行程序生成&#xff0c;数学计算是少不了的&#xff0c;Python支持一些常规的表达式计算&#xff0c;而另外一些相关的数学函数则在math模块中。 一些基础的运算 取余、除法、整除 >>> 21 % 4 1>>> 21 / 4 5.25>>> 21 // 4 5…

视频画面提取保存为图片:简易方法与实用工具

如果需要在视频里随机截取某一帧作为照片来保存或分享&#xff0c;如何快速剪辑多个视频&#xff1f;幸运的是&#xff0c;如今有多种简易的方法和实用的工具可以帮助我们轻松实现这一目标。 1打开“媒体梦工厂”用到“视频封面”功能&#xff0c; 2在此功能里切换到“抽帧/提取…

通过实时可视性转变云安全

Upwind首席执行官 Amiram Shachar 讨论了混合和多云环境中云安全的复杂性。 他概述了深入了解配置和实时洞察的必要性&#xff0c;以实现敏捷性和安全性之间的平衡。 还分享了解决错误配置和确保合规性的策略&#xff0c;建议在云部署中采取主动的风险管理方法。 随着混合云…