Linux内核多线程实现方法 —— kthread_create函数

2023-11-23 06:08

本文主要是介绍Linux内核多线程实现方法 —— kthread_create函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

      Linux内核多线程实现方法 —— kthread_create函数

 

内核经常需要在后台执行一些操作,这种任务就可以通过内核线程(kernle thread)完成独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。实际上,内核线程只能由其他内核线程创建,在现有的内核线程中创建一个新的内核线程的方法:

kthread_create:创建线程。
struct task_struct *kthread_create(int (*threadfn)(void *data),void *data,const char *namefmt, ...);//注意,第二个参数data用于向线程传递参数

线程创建后,不会马上运行,而是需要将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。

kthread_run :创建并启动线程的函数,相当于kthread_create +  wake_up_process功能;

struct task_struct *kthread_run(int (*threadfn)(void *data),void *data,const char *namefmt, ...);
kthread_stop:通过发送信号给线程,使之退出。
int kthread_stop(struct task_struct *thread);线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。 但如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止,因此,线程函数必须能让出CPU,以便能运行其他线程。同时线程函数也必须能重新被调度运行。在例子程序中,这是通过schedule_timeout()函数完成的(下面的例子会看到)。

 

1.      头文件

#include <linux/sched.h>  //wake_up_process()

#include <linux/kthread.h>//kthread_create()、kthread_run()

#include<err.h>             //IS_ERR()、PTR_ERR()

2.      实现

2.1创建线程

在模块初始化时,可以进行线程的创建。使用下面的函数和宏定义:

struct task_struct *kthread_create(int (*threadfn)(void *data),

                           void *data,

                           const char namefmt[], ...);

#define kthread_run(threadfn, data, namefmt,...)                    \

({                                                           \

    struct task_struct*__k                                       \

          = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \

    if(!IS_ERR(__k))                                       \

          wake_up_process(__k);                               \

   __k;                                                    \

})

例如

static struct task_struct *test_task;

static inttest_init_module(void)    //驱动加载函数

{

    int err;

    test_task = kthread_create(threadfunc, NULL, "test_task");

    if(IS_ERR(test_task)){

     printk("Unable to start kernel thread.\n");

      err = PTR_ERR(test_task);

      test_task =NULL;

      return err;

    }

wake_up_process(test_task);
       return 0;
   }

   module_init(test_init_module);

2.2线程函数

在线程函数里,完成所需的业务逻辑工作。主要框架如下所示:

int threadfunc(void *data){

       …

       while(1){

              set_current_state(TASK_UNINTERRUPTIBLE);//将当前的状态表示设置为休眠

if(kthread_should_stop()) break;  //解释见“注意”

              if(){//条件为真

                     //进行业务处理

              }

              else{//条件为假

                     //让出CPU运行其他线程,并在指定的时间内重新被调度

                    schedule_timeout(HZ);   // 休眠,与set_current_state配合使用,需要计算,这里表示休眠一秒

              }

       }

       …

       return 0;

}

注意:

a. 值得一提的是kthread_should_stop函数,我们需要在开启的线程中嵌入该函数并检查此函数的返回值,否则kthread_stop是不起作用的

b. 休眠有两种相关的状态:TASK_INTERRUPTIBLE and TASK_UNINTERRUPTIBLE。它们的惟一却不是处于TASK_UNINTERRUPTIBLE状态的进程会忽略信号,而处于TASK_INTERRUPTIBLE状态的进程如果收到信号会被唤醒并处理信号(然后再次进入等待睡眠状态)。两种状态的进程位于同一个等待队列上,等待某些事件,不能够运行。

c.schedule_time(s*HZ)的参数为节拍数,HZ宏每个系统定义不一样,表示每一秒时钟中断数,如在2.6中为1000,2.4中为100, s为秒单位,例如如果要休眠20ms,则schedule_time(0.02*HZ)就可以了。

2.3结束线程

在模块卸载时,可以结束线程的运行。使用下面的函数:

int kthread_stop(structtask_struct *k);

例如:

static void test_cleanup_module(void)

{

           if(test_task){

               kthread_stop(test_task);

               test_task = NULL;

           }

}

module_exit(test_cleanup_module);

3.       注意事项

(1)       在调用kthread_stop函数时,线程函数不能已经运行结束。否则,kthread_stop函数会一直进行等待。在执行kthread_stop的时候,目标线程必须没有退出,否则会Oops。原因很容易理解,当目标线程退出的时候,其对应的task结构也变得无效,kthread_stop引用该无效task结构就会出错。

(2)       线程函数必须能让出CPU,以便能运行其他线程。同时线程函数也必须能重新被调度运行。在例子程序中,这是通过schedule_timeout()函数完成的。

4.性能测试

可以使用top命令来查看线程(包括内核线程)的CPU利用率。命令如下:

       top–p 线程号

可以使用下面命令来查找线程号:

       psaux|grep 线程名


可以用下面的命令显示所有内核线程:
      ps afx

 

这篇关于Linux内核多线程实现方法 —— kthread_create函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/415887

相关文章

Qt实现对Word网页的读取功能

《Qt实现对Word网页的读取功能》文章介绍了几种在Qt中实现Word文档(.docx/.doc)读写功能的方法,包括基于QAxObject的COM接口调用、DOCX模板替换及跨平台解决方案,重点讨论... 目录1. 核心实现方式2. 基于QAxObject的COM接口调用(Windows专用)2.1 环境

MySQL查看表的历史SQL的几种实现方法

《MySQL查看表的历史SQL的几种实现方法》:本文主要介绍多种查看MySQL表历史SQL的方法,包括通用查询日志、慢查询日志、performance_schema、binlog、第三方工具等,并... 目录mysql 查看某张表的历史SQL1.查看MySQL通用查询日志(需提前开启)2.查看慢查询日志3.

MySQL底层文件的查看和修改方法

《MySQL底层文件的查看和修改方法》MySQL底层文件分为文本类(可安全查看/修改)和二进制类(禁止手动操作),以下按「查看方法、修改方法、风险管控三部分详细说明,所有操作均以Linux环境为例,需... 目录引言一、mysql 底层文件的查看方法1. 先定位核心文件路径(基础前提)2. 文本类文件(可直

Java实现字符串大小写转换的常用方法

《Java实现字符串大小写转换的常用方法》在Java中,字符串大小写转换是文本处理的核心操作之一,Java提供了多种灵活的方式来实现大小写转换,适用于不同场景和需求,本文将全面解析大小写转换的各种方法... 目录前言核心转换方法1.String类的基础方法2. 考虑区域设置的转换3. 字符级别的转换高级转换

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

MyBatis-Plus逻辑删除实现过程

《MyBatis-Plus逻辑删除实现过程》本文介绍了MyBatis-Plus如何实现逻辑删除功能,包括自动填充字段、配置与实现步骤、常见应用场景,并展示了如何使用remove方法进行逻辑删除,逻辑删... 目录1. 逻辑删除的必要性编程1.1 逻辑删除的定义1.2 逻辑删php除的优点1.3 适用场景2.

Linux内核定时器使用及说明

《Linux内核定时器使用及说明》文章详细介绍了Linux内核定时器的特性、核心数据结构、时间相关转换函数以及操作API,通过示例展示了如何编写和使用定时器,包括按键消抖的应用... 目录1.linux内核定时器特征2.Linux内核定时器核心数据结构3.Linux内核时间相关转换函数4.Linux内核定时

C#借助Spire.XLS for .NET实现在Excel中添加文档属性

《C#借助Spire.XLSfor.NET实现在Excel中添加文档属性》在日常的数据处理和项目管理中,Excel文档扮演着举足轻重的角色,本文将深入探讨如何在C#中借助强大的第三方库Spire.... 目录为什么需要程序化添加Excel文档属性使用Spire.XLS for .NET库实现文档属性管理Sp

检查 Nginx 是否启动的几种方法

《检查Nginx是否启动的几种方法》本文主要介绍了检查Nginx是否启动的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1. 使用 systemctl 命令(推荐)2. 使用 service 命令3. 检查进程是否存在4

Python+FFmpeg实现视频自动化处理的完整指南

《Python+FFmpeg实现视频自动化处理的完整指南》本文总结了一套在Python中使用subprocess.run调用FFmpeg进行视频自动化处理的解决方案,涵盖了跨平台硬件加速、中间素材处理... 目录一、 跨平台硬件加速:统一接口设计1. 核心映射逻辑2. python 实现代码二、 中间素材处