linux kthread_create 效率,ArrrKing的Linux国家地理之三——kthread_create引发的“血案”...

看MMC

Driver时看到了kthread_create()这个函数。感觉这家伙不是一般函数,有点来头,google之,果然大有搞头。把搜索到的文章copy如下:

[第一篇]说说内核线程(kthread_create 和

kernel_thread的区别就在于前者多加了个链表)

貌似很神秘的一个东西,我们就来"强行"揭开她的面纱......

说实话,我们不是第一次见她了!你说是不?(什么?不是?今天我们DOTA校队去和人家比赛,正因为没看上郁闷着呢,你还来给我添点油是吧...)

话说曾经我们在看serio.c的时候,遇到了这么一个模糊的面孔(说模糊是因为我们没对她过多深入):

serio_task =

kthread_run(serio_thread, NULL, "kseriod");

虽然犹抱琵琶半遮面,但是从侧面仍可以看出,这"应该"搞了个类似"thread"的东东

我们走进点:

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

})

哦,没什么大不了的嘛,就创建了一个内核线程并唤醒,恩,说对了!

其实说线程有点不妥,这个和我们平时所说的并和进程所区别的线程不一样,这里的线程本质上还是进程,不过linus说她是线程,你敢说啥?

我们具体来看看咋回事:

kthread.c

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

*data),

void *data,

const char namefmt[],

...)

{

struct

kthread_create_info create;

create.threadfn = threadfn;

create.data

= data;

init_completion(&create.started);

init_completion(&create.done);

spin_lock(&kthread_create_lock);

list_add_tail(&create.list,

&kthread_create_list);

wake_up_process(kthreadd_task);

spin_unlock(&kthread_create_lock);

wait_for_completion(&create.done);

if

(!IS_ERR(create.result)) {

va_list args;

va_start(args, namefmt);

vsnprintf(create.result->comm,

sizeof(create.result->comm),

namefmt, args);

va_end(args);

}

return

create.result;

}

首先,声明了一个struct kthread_create_info结构,这个结构包含了针对内核线程操作的所要用到的字段:

struct kthread_create_info

{

int

(*threadfn)(void *data);

void

*data;

struct

completion started;

struct

task_struct *result;

struct

completion done;

struct

list_head list;

};

其中包含了线程要干的活,同步用到的结构,以及线程的描述符,比如说函数最后返回的create.result,就是创建的内核"线程"的进程描述符.

然后初始化threadfn以及他的函数参数,将这个kthread_create_info结构挂入全局的内核线程队列,并唤醒进程kthreadd_task,开始等待,等待......

kthreadd_task是个什么玩意?这是在本文件头部声明的一个全局进程描述符,

在main.c中的rest_init里初始化:

static void noinline __init_refok rest_init(void)

__releases(kernel_lock)

{

int pid;

kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);

numa_default_policy();

pid = kernel_thread(kthreadd,

NULL, CLONE_FS | CLONE_FILES);

kthreadd_task = find_task_by_pid(pid);

unlock_kernel();

preempt_enable_no_resched();

schedule();

preempt_disable();

cpu_idle();

}

我们看到里面kernel_thread函数返回一个pid,然后我们通过这个pid得到了这个kthreadd_task的描述符.

其实我们的kthread_create这个函数并没有直接创建什么内核线程,这里的kernel_thread这里才是真正创建内核线程的所在:

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

flags)

{

struct

pt_regs regs;

memset(&regs, 0, sizeof(regs));

regs.ebx

= (unsigned long) fn;

regs.edx =

(unsigned long) arg;

regs.xds

= __USER_DS;

regs.xes =

__USER_DS;

regs.xfs =

__KERNEL_PERCPU;

regs.orig_eax = -1;

regs.eip =

(unsigned long) kernel_thread_helper;

regs.xcs =

__KERNEL_CS | get_kernel_rpl();

regs.eflags

= X86_EFLAGS_IF | X86_EFLAGS_SF | X86_EFLAGS_PF | 0x2;

return

do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,

&regs, 0, NULL, NULL);

}

do_fork(),一个伟大的函数,经过他的流程,一个新的"内核线程"诞生了...

我们来看看kthreadd_task到底都干了些什么:

int kthreadd(void *unused)

{

kthreadd_setup();

current->flags |= PF_NOFREEZE;

for (;;)

{

set_current_state(TASK_INTERRUPTIBLE);

if (list_empty(&kthread_create_list))

schedule();

__set_current_state(TASK_RUNNING);

spin_lock(&kthread_create_lock);

while (!list_empty(&kthread_create_list)) {

struct kthread_create_info *create;

create = list_entry(kthread_create_list.next,

struct kthread_create_info, list);

list_del_init(&create->list);

spin_unlock(&kthread_create_lock);

create_kthread(create);

spin_lock(&kthread_create_lock);

}

spin_unlock(&kthread_create_lock);

}

return

0;

}

写过驱动的哥们都知道,这个套路不陌生.整个一大循环,如果全局的kthread_create_list为空,我们这个内核线程就会一直沉睡,如果醒来发现还是空的怎么办?废话,当然是继续睡了!

当有一天这个线程醒了发现有事可做了,那么他就会从这个队列中取下一个他渴望的猎物,然后,然后,然后你说呢?static

void create_kthread(struct kthread_create_info *create)

{

int pid;

pid =

kernel_thread(kthread, create, CLONE_FS | CLONE_FILES |

SIGCHLD);

if (pid

< 0) {

create->result = ERR_PTR(pid);

} else

{

wait_for_completion(&create->started);

read_lock(&tasklist_lock);

create->result = find_task_by_pid(pid);

read_unlock(&tasklist_lock);

}

complete(&create->done);

}

我们的内核线程是在这里创建的,要做什么呢?答案在传进来的struct kthread_create_info

*create里.

然后我们对返回的pid检查,如果正常,那么我们就等待内核线程的对create->started的同步,来告诉父"线程",我们已经OK了.同样我们通过另一个同步信号create->done来告诉调用kthread_create的进程,你想要的东西已经准备好了!

static int kthread(void *_create)

{

struct

kthread_create_info *create = _create;

int

(*threadfn)(void *data);

void

*data;

int ret =

-EINTR;

threadfn =

create->threadfn;

data =

create->data;

__set_current_state(TASK_UNINTERRUPTIBLE);

complete(&create->started);

schedule();

if

(!kthread_should_stop())

ret = threadfn(data);

if

(kthread_should_stop()) {

kthread_stop_info.err = ret;

complete(&kthread_stop_info.done);

}

return

0;

}

我们在同步create->started后就让出了CPU,并等待唤醒.在我们的kthread_run里,我们不需要等待很长的时间,因为很快,我们就有 if

(!IS_ERR(__k)) wake_up_process(__k);

当然,我们写驱动的时候可以根据自己的情况,比如创建一个内核线程以后我们要做一些别的操作比如增加计数什么的,我们就不用这个宏,然后自己来唤醒内核线程.

[第二篇]kthread_create创建线程

1 使用kthread_create创建线程:

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

*data),

void

*data,

const char *namefmt, ...);

这个函数可以像printk一样传入某种格式的线程名

线程创建后,不会马上运行,而是需要将kthread_create()

返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。

2. 当然,还有一个创建并启动线程的函数:kthread_run

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

void *data,

const char *namefmt, ...);

3.

线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。

int kthread_stop(struct task_struct *thread);

kthread_stop() 通过发送信号给线程。

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

参考:Kernel threads made easy

--

view plaincopy to clipboardprint?

#include

 static struct task_struct *

_task; static struct task_struct *

_task2; static struct task_struct *

_task3; static int thread_func(void

*data) { int j,k; int timeout; wait_queue_head_t

timeout_wq; static int i =

0; i++; j = 0; k = i; printk("thread_func %d started\n",

i); init_waitqueue_head(&timeout_wq); while(!kthread_should_stop()) { interruptible_sleep_on_timeout(&timeout_wq,

HZ); printk("[%d]sleeping..%d\n", k,

j++); } return 0; } void

my_start_thread(void) { //_task = kthread_create(thread_func, NULL,

"thread_func2"); //wake_up_process(_task); _task = kthread_run(thread_func, NULL,

"thread_func2"); _task2 = kthread_run(thread_func, NULL,

"thread_func2"); _task3 = kthread_run(thread_func, NULL,

"thread_func2"); if (!IS_ERR(_task)) { printk("kthread_create

done\n"); } else { printk("kthread_create

error\n"); } } void my_end_thread(void) { int ret = 0; ret =

kthread_stop(_task); printk("end thread. ret = %d\n" ,

ret); ret =

kthread_stop(_task2); printk("end thread. ret = %d\n" ,

ret); ret =

kthread_stop(_task3); printk("end thread. ret = %d\n" ,

ret); } #include

static struct task_struct * _task;

static struct task_struct * _task2;

static struct task_struct * _task3;

static int thread_func(void *data)

{

int j,k;

int timeout;

wait_queue_head_t timeout_wq;

static int i =

0; i++;

j = 0;

k = i;

printk("thread_func %d started\n", i);

init_waitqueue_head(&timeout_wq);

while(!kthread_should_stop())

{

interruptible_sleep_on_timeout(&timeout_wq,

HZ);

printk("[%d]sleeping..%d\n", k, j++);

}

return 0;

}

void my_start_thread(void)

{

//_task = kthread_create(thread_func, NULL, "thread_func2");

//wake_up_process(_task);

_task = kthread_run(thread_func, NULL, "thread_func2");

_task2 = kthread_run(thread_func, NULL, "thread_func2");

_task3 = kthread_run(thread_func, NULL, "thread_func2");

if (!IS_ERR(_task))

{

printk("kthread_create done\n");

}

else

{

printk("kthread_create error\n");

}

}

void my_end_thread(void)

{

int ret = 0;

ret = kthread_stop(_task);

printk("end thread. ret = %d\n" , ret);

ret = kthread_stop(_task2);

printk("end thread. ret = %d\n" , ret);

ret = kthread_stop(_task3);

printk("end thread. ret = %d\n" , ret);

}

在执行kthread_stop的时候,目标线程必须没有退出,否则会Oops。原因很容易理解,当目标线程退出的时候,其对应的task结构也变得无效,kthread_stop引用该无效task结构就会出错。

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

thread_func()

{

// do

your work here

// wait

to exit

while(!thread_could_stop())

{

wait();

}

}

exit_code()

{

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

}

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

[第三篇]kthread_create创建的内核线程中如何调用down?

标题: kthread_create创建的内核线程中如何调用down?

如果用kernel_thread创建线程,在其中使用down和up很方便,但是通过kthread_create创建了线程A,然后通过down和up来控制其运行和睡眠状态,但是在模块卸载的时候确发现无法通过kthread_stop终止线程,请问有什么办法解决?还是只能通过schedule?

主要想让A循环处理命令队列Q,但是Q中的某些命令状态并不是都要处理,比如有状态1,2,3,4,5,只有状态1,5需要处理,但是如果让A不停遍历队列Q处理则效率很低,想让A每次遍历完一次队列即睡眠,当有状态改变时才唤醒A,有什么好办法嘛?

*********************************

我没有理解你的问题。

但是线程应该可以自然退出的, 你看看xfs的实现就知道了。

module加载的时候会建立几个线程。

有某个全局变量 __quited;

线程检查 __quited 是否为假否则作清理工作, 并退出。

rmmod的时候只需要将 __quited设置为真, 并等待线程的退出, 之后就返回了。

*********************************

你内核线程里的无限循环改为

while (!kthread_should_stop())

{

.........

}

kthread_should_stop()会去判断是不是有某人调用了kthread_stop去终止你的内核线程,如果是就会退出无限循环,

然后内核会通知kthread_stop,表明线程退出了,kthread_stop函数等到线程退出后才会返回.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值