分类: LINUX
2012-10-12 16:52:01
原文地址:http://blog.sina.com.cn/s/blog_6237dcca0100gq67.html
貌似很神秘的一个东西,我们就来"强行"揭开她的面纱......
说实话,我们不是第一次见她了!你说是不?(什么?不是?今天我们DOTA校队去和人家比赛,正因为没看上郁闷着呢,你还来给我添点油是吧...)
kernel
thread可以用kernel_thread创建,但是在执行函数里面必须用daemonize释放资源并挂到init下,还需要用completion等待这一过程的完成。
kthread_create是比较正牌的创建函数,这个不必要调用daemonize,用这个创建的kernel
thread都挂在了kthread线程下。
话说曾经我们在看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(®s, 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,
®s, 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);
当然,我们写驱动的时候可以根据自己的情况,比如创建一个内核线程以后我们要做一些别的操作比如增加计数什么的,我们就不用这个宏,然后自己来唤醒内核线程.