Linux设备驱动程序中创建线程的方法

参考博客文章来源:https://blog.csdn.net/ezimu/article/details/60467017

第一种方法:kernel_thread

#include <linux/sched.h>

extern pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);

(1)参数说明:

fn线程函数地址
arg线程函数的形参,没有,可以是NULL
flags标志,一般用CLONE_KERNEL(定义在linux/sched.h中,注意有的版本中,CLONE_FS | CLONE_FILES | CLONE_SIGHAND),其他标志及含义见uapi/linux/sched.h中
返回值pid_t返回线程ID值

(2)使用例子:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/delay.h>

MODULE_AUTHOR("pyl");
MODULE_LICENSE("GPL");

#define CLONE_KERNEL    (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)

int my_kernel_thread(void *arg)
{
        int n = 0;

        while(1)
        {
                printk("%s: %d\n",__func__,n++);
                ssleep(3);
        }

        return 0;
}
static int __init practice_call(void)
{
        printk("%s:\n",__func__);

//创建一个线程,并开始执行
        kernel_thread(my_kernel_thread,NULL,CLONE_KERNEL);    

        return 0;
}

static void __exit practice_call_exit(void)
{
        printk("%s:\n",__func__);
}

module_init(practice_call);
module_exit(practice_call_exit);

第二种方法:kthread_create

#include <linux/kthread.h>

struct task_struct *kthread_create_on_node(
                   int (*threadfn)(void *data), 
                   void *data,int node,
                   const char namefmt[], ...);

/**
 * kthread_create - create a kthread on the current node
 * @threadfn: the function to run in the thread
 * @data: data pointer for @threadfn()
 * @namefmt: printf-style format string for the thread name
 * @...: arguments for @namefmt.
 *
 * This macro will create a kthread on the current node, leaving it in
 * the stopped state.  This is just a helper for kthread_create_on_node();
 * see the documentation there for more details.
 */
#define kthread_create(threadfn, data, namefmt, arg...) \
        kthread_create_on_node(threadfn,
                               data,
                               namefmt, ##arg)

(1)参数说明:

threadfn

线程函数地址
data线程函数形参,如果没有可以定义为NULL
namefmt,arg…线程函数名字,可以格式化输出名字
返回值(strcut task_struct *)线程指针

注意:kthread_create()创建后,线程没有立即运行,需要将返回的值,即线程指针(struct task_struct *),作为参数传入到wake_up_process()唤起线程运行。

(2)使用例子:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>

MODULE_AUTHOR("pyl");
MODULE_LICENSE("GPL");

#define CLONE_KERNEL    (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)

//线程指针
strcut task_struct *practice_task_p = NULL;

int my_kernel_thread(void *arg)
{
        int n = 0;

        while(1)
        {
                printk("%s: %d\n",__func__,n++);
                ssleep(3);

//kthread_should_stop判断线程是否应该结束,返回true表示结束,false表示不结束
        if(kthread_should_stop())
                {
                        break;
                }

        }

        return 0;
}
static int __init practice_call(void)
{
        printk("%s:\n",__func__);
        practice_task_p = kthread_create(my_kernel_thread,NULL,"practice task");    
        if(!IS_ERR(practice_task_p))
                wake_up_process(practice_task_p);//唤醒practice_task_p指向的线程
        return 0;
}

static void __exit practice_call_exit(void)
{
        printk("%s:\n",__func__);
        kthread_stop(practice_task_p);//停止线程,kthread_should_stop会做出响应
}

module_init(practice_call);
module_exit(practice_call_exit);

注意:在编写线程循环体时,一般都要加入kthread_should_stop(),如果不加,调用kthread_stop()是没有效果的,也会导致模块退出后,线程仍然还在运行。

第三种方法:kthread_run

#include <linux/kthread.h>
/**
 * kthread_run - create and wake a thread.
 * @threadfn: the function to run until signal_pending(current).
 * @data: data ptr for @threadfn.
 * @namefmt: printf-style name for the thread.
 *
 * Description: Convenient wrapper for kthread_create() followed by
 * wake_up_process().  Returns the kthread or ERR_PTR(-ENOMEM).
 */
#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;                                                               \
})

(1)参数说明:

threadfn

线程函数地址
data线程函数形参,没有可以制定为NULL
namefmt, …线程名字,可以格式化输出
返回值__k线程结构体指针(struct task_struct * )

注意:kthread_run()创建的线程,是立刻运行的。

(2)使用例子:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>

MODULE_AUTHOR("zimu");
MODULE_LICENSE("GPL");

#define CLONE_KERNEL    (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)

int my_kernel_thread(void *arg)
{
        int n = 0;

        while(1)
        {
                printk("%s: %d\n",__func__,n++);
                ssleep(3);
        if(kthread_should_stop())
                {
                        break;
                }

        }

        return 0;
}
static int __init practice_call(void)
{
        printk("%s:\n",__func__);

        practice_task_p = kthread_run(my_kernel_thread,NULL,"practice task");    

        return 0;
}

static void __exit practice_call_exit(void)
{
        printk("%s:\n",__func__);
        kthread_stop(practice_task_p);
}

module_init(practice_call);
module_exit(practice_call_exit);

注意:kernel_thread()的参数fn,以及kthread_create()/kthread_run()的参数threadfn即使是一样的函数,只要创建的线程不一样(线程名字不一样),那么fn,threadfn函数在不同的线程里面,运行的空间地址是不一样的。


                                                                线程状态


  一、线程park状态

(1)park与stop的区别:

线程的stop(kthread_stop())状态指线程终止,线程的生命周期结束
线程的park状态(kthread_park())指线程暂时停止(可以理解就是挂起),当执行换醒线程(kthread_unpark())后,线程从挂起状态唤醒,接着运行

(2)park的挂起与唤醒函数

挂起kthread_park
唤醒kthread_unpark

(3)使用例子

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/kthread.h>

MODULE_AUTHOR("zimu");
MODULE_LICENSE("GPL");

#define CLONE_KERNEL    (CLONE_FS | CLONE_FILES | CLONE_SIGHAND)

int my_kernel_thread(void *arg)
{
        int n = 0;

        while(1)
        {
                printk("%s: %d\n",__func__,n++);
                ssleep(3);

        if(kthread_should_park())
            kthread_parkme();

        if(kthread_should_stop())
                {
                        break;
                }

        }

        return 0;
}
static int __init practice_call(void)
{
        printk("%s:\n",__func__);
        practice_task_p = kthread_run(my_kernel_thread,NULL,"practice task");    

        return 0;
}

static void __exit practice_call_exit(void)
{
        printk("%s:\n",__func__);
        kthread_stop(practice_task_p);
}

module_init(practice_call);
module_exit(practice_call_exit);

(4)park状态判断

if(kthread_should_park())
    kthread_park();

kthread_should_park()函数判断了调用kthread_park()函数的该线程是否应该挂起,返回true就挂起,false就是不挂起

二、线程的freez状态

1、进程(线程)冻结技术(freezing of tasks)是指在系统hibernate或者suspend的时候,将用户进程和部分内核线程置于“可控”的暂停状态。
2、当系统进入hibernate或者suspend的时候,线程如果要响应,那么线程需要使用相应接口将线程冻结。

(1)接口函数

#include <linux/freezer.h>

static inline bool try_to_freeze(void);
static inline bool freezing(struct task_struct *p);

#include <linux/kthread.h>

bool kthread_freezable_should_stop(bool *was_frozen);

(2)使用方法:

第一种:kthread_freezable_should_stop(bool *was_frozen)函数

int my_kernel_thread(void *arg)
{
        int n = 0;
        bool free;

        while(1)
        {
                printk("%s: %d\n",__func__,n++);
                ssleep(3);

//会一直阻塞在这里,知道系统被唤醒
//kthread_freezable_should_stop函数会判断这个线程唤醒后是否需要停止
        if(kthread_freezable_should_stop())
        {
            break;
        }

        }

        return 0;
}
kthread_freezable_should_stop如果系统进入hibernate或者suspend,将当前线程挂起,并置位PF_FROZEN标志,调用schedule()放弃CPU控制全,直到系统退出hibernate或者suspend,或者其他唤起,kthread_freezable_should_stop才会返回,否则将一直不返回。
kthread_freezable_should_stop为了安全,返回的是线程是否需要退出真值(true,false),返回的was_frozen值,true表示线程是从冻结后唤醒退出的,false表示没有被冻结。

第二种:

int my_kernel_thread(void *arg)
{
        int n = 0;
        bool free;

        while(1)
        {
                printk("%s: %d\n",__func__,n++);
                ssleep(3);

        if(freezing(current))
        {
            try_to_freeze();
        }
        if(kthread_should_stop())
                {
                        break;
                }

        }

        return 0;
}
freezing()判断系统是否进入冻结,ture冻结,false没有冻结
try_to_freeze()将当前线程冻结,冻结后将不会返回,直到解冻,或者其他唤起,否则一直不返回

总结:线程是否要响应系统的冻结状态,需要根据情况,看系统冻结后,驱动模块是否需要运行

三、线程绑定

#include <linux/kthread.h>

void kthread_bind(struct task_struct *k, unsigned int cpu);
功能将线程和CPU绑定,这个线程只在绑定的CPU上运行,在多核CPU上用
k线程结构体指针
cpu要绑定的CPU号

 

 

最后总结:在驱动程序中创建一个线程,首先使用线程创建函数生产一个线程,然后使用线程执行函数后者结束函数控制线程开始于停止,也可以使用状态函数让线程处于挂起状态和运行状态,在线程的执行函数中使用状态函数获取线程的当前状态,用于确定具体要执行的任务。线程的执行函数中是一个无线循环体,在循环体里面加入线程的状态判断或获取函数,结合需要,在对应的条件下执行操作,比如停止线程操作就是一个跳出循环的break语句,改变或判断线程状态就是先使用线程的kthread_should_stop或kthread_should_park函数作为执行操作的条件,如果条件符合,就在这个条件的代码段使用线程的控制函数。

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值