內核線程kthreadd詳解

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任務

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章