admin管理员组文章数量:1623794
驱动开发中常常会启动几个内核线程,在整个驱动生命周期期间执行某些操作,比如USB驱动的控制线程,一直等待SCSI命令,没有命令的话睡眠,有命令的话就唤醒线程,解析执行相关的命令。还有USB驱动中的扫描线程,如果有新的设备连接到USB总线,则会启动扫描过程,平时时候让出CPU资源休眠。
常用的内核线程创建方法有3个,kernel_thread, kthread_create和kthread_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:函数指针,指向要创建的内核线程的执行函数
arg:fn函数的参数,可以没有,没有写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:创建的内核线程的进程描述符
cpu:CPU编号
(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() 通过发送信号给线程。
如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。
转自:http://dashan8020.blog.163/blog/static/4796750420115180227132/
版权声明:本文标题:Linux kernel多线程的几种实现 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1728889570a1177937.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论