linux在初始化的時候,除了靜態的idle線程,還會創建kernel_init線程和kthreadd線程。kthreadd線程爲2號線程,該線程專門用來負責爲kernel創建其他線程。下面看一下如何利用kthreadd創建一個內核線程。
struct kthread_create_info
{
/* Information passed to kthread() from kthreadd. */
int (*threadfn)(void *data); //要創建的線程的執行函數
void *data;
int node;
/* Result passed back to kthread_create() from kthreadd. */
struct task_struct *result; //用來向線程申請者返回task_struct
struct completion done;//向申請者通知創建完成
struct list_head list;//掛載進kthreadd的處理隊列
};
爲了容易區分,我們把需要創建新線程的叫做申請者,具體負責創建新進程的叫做執行者,這邊執行者就是kthreadd線程。kthread_create_info數據結構用來在申請者和執行者之間傳遞對象。
1 新線程創建的申請
struct kthread_create_info create;
struct task_struct *task;
create.threadfn = threadfn;//新建線程的執行函數
create.data = data;
create.node = node;
init_completion(&create.done);//初始化完成量
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);//添加到kthreadd執行隊列
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task);//喚醒kthreadd線程
wait_for_completion(&create.done);//等待kthreadd線程完成線程創建
task=create.result;//返回新建線程的描述符
wake_up_process(task);//喚醒新建線程
2 新線程創建
kthreadd_task是kthreadd線程的進程描述符,在系統初始化的時候創建:
static noinline void __init_refok rest_init(void)
{
........................................
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
........................................
}
下面看一下kthreadd的具體實現:
int kthreadd(void *unused)
{
struct task_struct *tsk = current;
/* Setup a clean context for our children to inherit. */
set_task_comm(tsk, "kthreadd");
ignore_signals(tsk);
set_cpus_allowed_ptr(tsk, cpu_all_mask);
set_mems_allowed(node_states[N_MEMORY]);
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);//先從隊列上刪除該create
spin_unlock(&kthread_create_lock);
create_kthread(create);//爲申請者創建線程
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
可以看到,在kthread_create_list鏈表中獲取到申請者傳過來的kthread_create_info結構,利用該信息調用create_kthread來創建線程。
static void create_kthread(struct kthread_create_info *create)
{
int pid;
#ifdef CONFIG_NUMA
current->pref_node_fork = create->node;
#endif
/* We want our own signal handler (we take no signals by default). */
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
if (pid < 0) {
create->result = ERR_PTR(pid);
complete(&create->done);
}
}
調用kernel_thread創建kthread線程,參數爲create,看一下kernel_thread是如何執行的:
static int kthread(void *_create)
{
/* Copy data: it's on kthread's stack */
struct kthread_create_info *create = _create;
int (*threadfn)(void *data) = create->threadfn;
void *data = create->data;
struct kthread self;
int ret;
self.flags = 0;
self.data = data;
init_completion(&self.exited);
init_completion(&self.parked);
current->vfork_done = &self.exited;
/* OK, tell user we're spawned, wait for stop or wakeup */
__set_current_state(TASK_UNINTERRUPTIBLE);
create->result = current;//向申請者返回當前線程的描述符
complete(&create->done);//告訴申請者,線程創建完成
schedule();
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
__kthread_parkme(&self);
ret = threadfn(data);//申請者提供的線程執行函數
}
/* we can't just return, we must preserve "self" on stack */
do_exit(ret);
}
可以看到kthread是kthreadd函數創建的線程的入口地址,該函數最終執行到申請者提供的的threadfn函數,至此創建者完成了自己的使命,申請者開始有了自己的新線程,並執行threadfn任務