Linux kernel多线程的几种实现

驱动开发中常常会启动几个内核线程,在整个驱动生命周期期间执行某些操作,比如USB驱动的控制线程,一直等待SCSI命令,没有命令的话睡眠,有命令的话就唤醒线程,解析执行相关的命令。还有USB驱动中的扫描线程,如果有新的设备连接到USB总线,则会启动扫描过程,平时时候让出CPU资源休眠。

常用的内核线程创建方法有3个,kernel_thread, kthread_createkthread_run。使用这些函数或宏需要包括如下头文件:

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

#include <linux/kthread.h>//kthread_ceate(), kthread_run()

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

 

这些方法创建的内核线程必须能够自己放弃CPU资源,即不要产生死循环而不主动调用scheduel()函数,否则这样CPU会一直忙,因为内核进程/线程是不可抢占的,所以他必须自己能够主动的放弃资源,不管通过什么方式。

(1)    kernel_thread

原型如下

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

该函数的实现与具体的CPU架构有关,各参数的含义如下:

fn:函数指针,指向要创建的内核线程的执行函数

argfn函数的参数,可以没有,没有写NULL

flags:标志,与创建他的进程共享哪些东西

该函数返回整型数,如果返回的值小于0则表示内核线程创建失败,否则创建成功。

由该函数创建的进程不需要在模块清除时注销,可能运行过就自动销毁了。

例子:

static DECLARE_WAIT_QUEUE_HEAD(my_waitqueue);

static int example_kernel_thread(void)

{

       DECLARE_WAITQUEUE(wait,current);

       daemonize(“new_kthread”);

       allow_signal(SIGKILL);

       add_wait_queue(&my_waitqueue,&wait);

      

       while(1)

       {

              set_current_state(TASK_INTERRUPTIBLE);

              schedule();

              if(signal_pending(current))

                     break;

}

set_current_state(TASK_RUNNING);

remove_wait_queue(&my_waitqueue,&wait);

printk(KERN_NOTICE “new thread running\n”);

return 0;

}

static __init int init_hello_world(void)

{

       int status;

       status = kernel_thread(example_kernel_thread, NULL, CLONE_FS)

       if(status < 0)

              printk(KERN_NOTICE “kernel thread create failed\n”);

       else

              printk(KERN_NOTICE “kernel thread create success\n”);

      

return 0;

}

 

static __exit void exit_hello_world(void)

{

       printk(KERN_NOTICE “exit module\n”);

       return;

}

 

module_init(init_hello_world);

module_exit(exit_hello_world);

(2)    kthread_create

原型如下:

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

                               void *data,

                               const char namefmt[], ...)

       __attribute__((format(printf, 3, 4)));

该函数返回创建的内核线程的进程描述符。

各参数含义:

threadfn:函数指针,指向内核线程所执行的函数

data:不定类型的指针,指向内核线程所需要的参数

namefmt:内核线程名字

:类似于printf参数

例如内核线程的名字带有不确定数字,可以像printf函数一样将数字写进内核线程名字。

 

这个函数创建的内核线程不能立即执行,需要调用wake_up_process()函数来使线程执行,为此定义了宏kthread_run,他是kthread_create()函数和wake_up_process()的封装。

例子:

static struct task_struct *test_task;

static __init int test_init_module(void)

{

       int err;

       int num = 1;

       test_task = kthread_create(test_thread,NULL,”test_task-%d”, num);

       if(IS_ERR(test_task))

{

       printk(KERN_NOTICE “create new kernel thread failed\n”);

       err = PTR_ERR(test_task);

       test_task = NULL;

       return err;

}

wake_up_process(test_task);

return 0;

}

 

模块退出时,需要结束所创建线程的运行,使用下面的函数:

int kthread_stop(struct task_struct *k);

int kthread_should_stop(void);

注意:在调用kthread_stop函数时,线程函数不能已经运行结束。否则,kthread_stop函数会一直进行等待。

为了避免这种情况,需要确保线程没有退出,其方法如代码中所示:

thread_func()

{

    // do your work here

    // wait to exit

    while(!thread_should_stop())

    {

           wait();

    }

}

exit_code()

{

     kthread_stop(_task);   //发信号给task,通知其可以退出了

}

这种退出机制很温和,一切尽在thread_func()的掌控之中,线程在退出时可以从容地释放资源,而不是莫名其妙地被人“暗杀”。

 

例子代码:

static void test_cleanup_module(void)

{

       if(test_task)

{

       kthread_stop(test_task);

       test_task = NULL;

}

}

module_init(test_init_module);

module_exit(test_cleanup_module);

 

关于创建的新线程的线程函数,该函数是要来完成所需要的业务逻辑工作的,其一般框架如下:

int threadfunc(void *data)

{

       ……..

       while(1)

{

       set_current_state(TASK_UNINTERRUPTIBLE);

       //wait_event_interruptible(wq,condition),或wait_for-completion(c)也可以

       if(kthread_should_stop())

              break;

       if(condition)//

       {

              ………//业务处理

}

else//

{

       //让出CPU,并在指定的时间内重新调度

       schedule_timeout(HZ);//延时1秒,重新调度执行

}

}

}

另外,有该函数创建的内核线程可以指定在不同的CPU核上运行(如果是多核的话),这个可以通过下面的函数实现:

void kthread_bind(struct task_struct *k, unsigned int cpu);

k:创建的内核线程的进程描述符

cpuCPU编号

(3)    kthread_run

这个不是函数,是一个宏:

/**

 * 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;                                                     \

})

这个宏完全同kthread_create(),只是创建完了线程,线程能够马上运行。

 

对于(2)(3)需要注意:

线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。 
    int kthread_stop(struct task_struct *thread);
kthread_stop() 
通过发送信号给线程。 

如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。


from: http://dashan8020.blog.163.com/blog/static/4796750420115180227132/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值