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

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐