linux kthread

前言

 

Linux下有3个特殊的进程,idle进程(PID = 0), init进程(PID = 1)和kthreadd(PID = 2):

a.  PID=0  系统自动创建、运行在内核态;

b.  PID=1  由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程;

c. PID=2 它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程。

 

1. kthreadd线程

路径:\linux-3.10.x\init\main.c  start_kernel()-->rest_init();

 

static noinline void __init_refok rest_init(void)
{
	int pid;

	rcu_scheduler_starting();
	/*
	 * We need to spawn init first so that it obtains pid 1, however
	 * the init task will end up wanting to create kthreads, which, if
	 * we schedule it before we create kthreadd, will OOPS.
	 */
	kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
	numa_default_policy();
	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();
	complete(&kthreadd_done); //唤醒完成量

	/*
	 * The boot idle thread must execute schedule()
	 * at least once to get things moving:
	 */
	init_idle_bootup_task(current);
	schedule_preempt_disabled();
	/* Call into cpu_idle with preempt disabled */
	cpu_startup_entry(CPUHP_ONLINE);
}

pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); kthread线程创建,源码如下:

 

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); //运行kthread在任意cpu上运行
	set_mems_allowed(node_states[N_MEMORY]);

	current->flags |= PF_NOFREEZE;

	for (;;) {
		set_current_state(TASK_INTERRUPTIBLE); //设置当前进程为中断模式,即可被cpu打断
		if (list_empty(&kthread_create_list)) //kthread_create_list 链表是否为空,为空就调用schedule()让出cpu进入休眠
			schedule();
		__set_current_state(TASK_RUNNING); //到这里表示此进程已从休眠状态恢复,即kthread_create_list链表不为空,设置当前进程为运行态

		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链表进行添加的, 在链表里主要是线程执行函数、线程参数进行绑定:

 

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
					   void *data, int node,
					   const char namefmt[],
					   ...)
{
	struct kthread_create_info create;

	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); //加入到kthread_create_list链表中
	spin_unlock(&kthread_create_lock);

	wake_up_process(kthreadd_task); //唤醒kthreadd_task内核任务线程
	wait_for_completion(&create.done); //等待threadfn线程完成//至此线程创建完毕 , 会唤醒kthread_create_on_node()函数内的完成量wait_for_completion(&create.done);

	if (!IS_ERR(create.result)) {
		static const struct sched_param param = { .sched_priority = 0 };
		va_list args;

		va_start(args, namefmt);
		vsnprintf(create.result->comm, sizeof(create.result->comm),
			  namefmt, args);
		va_end(args);
		/*
		 * root may have changed our (kthreadd's) priority or CPU mask.
		 * The kernel thread should not inherit these properties.
		 */
		sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m);
		set_cpus_allowed_ptr(create.result, cpu_all_mask);
	}
	return create.result;
}
EXPORT_SYMBOL(kthread_create_on_node);

 

通过create_kthread(create),完成真正的进程创建:

 

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, CLONE_FS | CLONE_FILES | SIGCHLD)完成create线程的创建:

 

 

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; //current 表示当前新创建的 thread 的 task_struct 结构
	complete(&create->done); //至此线程创建完毕 , 会唤醒kthread_create_on_node()函数内的等待完成量wait_for_completion(&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_create_on_node()函数内的等待完成量wait_for_completion(&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);
}

 

线程创建完毕:
创建新 thread 的进程恢复运行 kthread_create() 并且返回新创建线程的任务描述符 新创建的线程由于执行了 schedule() 调度,此时并没有执行.直到我们使用wake_up_process(p);唤醒新创建的线程线程被唤醒后, 会接着执行threadfn(data)
 

总结:

     kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理,它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程我们在内核中通过kernel_create或者其他方式创建一个内核线程, 然后kthreadd内核线程被唤醒, 来执行内核线程创建的真正工作,新的线程将执行kthread函数, 完成创建工作,创建完毕后让出CPU,因此新的内核线程不会立刻运行.需要手工 wake up, 被唤醒后将执行自己的真正工作函数。


任何一个内核线程入口都是 kthread()

通过 kthread_create() 创建的内核线程不会立刻运行.需要手工 wake up.

通过 kthread_create() 创建的内核线程有可能不会执行相应线程函数threadfn而直接退出

 

 

 


 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值