Android 8.0 开机流程 (二) Linux 内核kthreadd进程的启动

上一篇文章中分析了Linux内核idle进程(pid=0)的启动启动过程,并且创建了kthreadd 进程和init进程。init进程较为复杂,放在下一篇分析。本文就先分析kthreadd进程的启动过程。

1、kthreadd的创建

kthreadd定义在 Kernel\kernel\kthread.c

在上文中得知,kthreadd进程在rest_init()中被创建。task_struct结构体是进程描述符,里面包括了pid thread_info和state 进程状态等信息。

                  状态                                                                               描述
TASK_RUNNING表示进程正在执行或者处于准备执行的状态
TASK_INTERRUPTIBLE进程因为等待某些条件处于阻塞(挂起状态),一旦等待的条件成立,进程便会从该状态转化成就绪状态
TASK_UNINTERRUPTIBLE意思与TASK_INTERRUPTIBLE类似,但是我们传递任意信息等不能唤醒他们,只有它所等待的资源可用的时候,才会被唤醒
TASK_STOPPED进程被停止执行
TASK_TRACED进程被debugger等进程所监视
EXIT_ZOMBIE进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息,此时进程成为僵尸进程
EXIT_DEAD进程被杀死,即进程的最终状态
TASK_KILLABLE当进程处于这种可以终止的新睡眠状态中,他的运行原理类似于TASK_UNINTERRUPTIBLE,只不过可以响应致命信号
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);//运行kthreadd在任意CPU运行
	set_mems_allowed(node_states[N_MEMORY]);

	current->flags |= PF_NOFREEZE;
	cgroup_init_kthreadd();

	for (;;) {
        //设置当前进程状态为TASK_INTERRUPTIBLE,
		set_current_state(TASK_INTERRUPTIBLE);
        //查找kthread_create_list列表中 是否有新需创建的线程,如果没有让出CPU,进入睡眠
		if (list_empty(&kthread_create_list))
			schedule();
        //如果有要创建的线程,设置当前进程状态为TASK_INTERRUPTIBLE 运行态
		__set_current_state(TASK_RUNNING);
        //spin_lock自旋锁,不可睡眠
		spin_lock(&kthread_create_lock);
        //查找kthread_create_list列表
		while (!list_empty(&kthread_create_list)) {
            //kthread_info
			struct kthread_create_info *create;
            //从kthread_create_list中取出要创建线程的信息
			create = list_entry(kthread_create_list.next,
					    struct kthread_create_info, list);
            //从列表中删除要创建的线程
			list_del_init(&create->list);
            //spin_unlock 解锁
			spin_unlock(&kthread_create_lock);
            //创建线程
			create_kthread(create);
            //spin_lock自旋锁,不可睡眠   
			spin_lock(&kthread_create_lock);
		}
         //spin_unlock 解锁
		spin_unlock(&kthread_create_lock);
	}

	return 0;
}

在kthreadd循环中,从kthread_create_list列表中查看是否有要创建的线程。如果没有,让出cpu,进入睡眠状态。如果有调用create_kthread()创建线程。

2.create_kthread

create_kthread 定义在 Kernel\kernel\kthread.c

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). */
    //调用kernel_thread创建线程
	pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
	if (pid < 0) {
		/* If user was SIGKILLed, I release the structure. */
		struct completion *done = xchg(&create->done, NULL);

		if (!done) {
			kfree(create);
			return;
		}
		create->result = ERR_PTR(pid);
		complete(done);
	}
}

在create_kthread 中调用 kernel_thread()创建线程。然后执行kthread函数。kthreadd是内核pid=2的线程守护进程。kthread只是一个线程函数。注意

3.kthread

kthread 定义在 Kernel\kernel\kthread.c

static int kthread(void *_create)
{
	/* Copy data: it's on kthread's stack */
    //create 是之前创建线程的信息
	struct kthread_create_info *create = _create;
	int (*threadfn)(void *data) = create->threadfn;//新线程执行函数
	void *data = create->data;
	struct completion *done;
	struct kthread self;
	int ret;

	self.flags = 0;
	self.data = data;
	init_completion(&self.exited);
	init_completion(&self.parked);
	current->vfork_done = &self.exited;

	/* If user was SIGKILLed, I release the structure. */
	done = xchg(&create->done, NULL);
	if (!done) {
		kfree(create);
		do_exit(-EINTR);
	}
	/* OK, tell user we're spawned, wait for stop or wakeup */
	__set_current_state(TASK_UNINTERRUPTIBLE);
	create->result = current;
	complete(done);//线程创建完成
     //让出CPU,注意这里并没有执行新线程的threadfn函数就直接进入睡眠了,
    //然后等待线程被手动唤醒,然后才执行threadfn
	schedule();

	ret = -EINTR;

	if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
		cgroup_kthread_ready();
		__kthread_parkme(&self);
		ret = threadfn(data);
	}
	/* we can't just return, we must preserve "self" on stack */
	do_exit(ret);
}

kthred创建新线程,后进入睡眠状态。需手动唤醒线程并执行相应的函数。kthreadd进程分析完,死循环从kthread_create_list()列表中查找新线程,是否需要创建新的线程。

4.创建线程的方式

Linux 内核中创建线程有两种方式,一种是上文提到的kthread_create(),不过kthread_create()创建的线程并不会唤醒,需手动唤醒。kthread_run()创建的线程会唤醒。

kthread_create()和kthread_run() 定义在 Kernel\include\linux\kthread.h

kthread_create()和kthread_run()是Linux 的俩个宏

#define kthread_create(threadfn, data, namefmt, arg...) \
	kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)


/*kthread_run 调用kthread_create 并后面添加了wake_up_process唤醒功能*/
#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;								   \
})

 kthread_create和kthread_run并不是函数,而是宏定义,在编译时会替换对应代码,宏的参数没有类型定义,多行宏的定义会在行末尾加上\ 

5.kthread_create_on_node

kthread_create_on_node定义在 Kernel\kernel\kthread.c

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

	va_start(args, namefmt);
    //调用__kthread_create_on_node
	task = __kthread_create_on_node(threadfn, data, node, namefmt, args);
	va_end(args);

	return task;
}

static struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
						    void *data, int node,
						    const char namefmt[],
						    va_list args)
{
	DECLARE_COMPLETION_ONSTACK(done);
	struct task_struct *task;
    //申请create空间
	struct kthread_create_info *create = kmalloc(sizeof(*create),
						     GFP_KERNEL);
    //赋值
	if (!create)
		return ERR_PTR(-ENOMEM);
	create->threadfn = threadfn;
	create->data = data;
	create->node = node;
	create->done = &done;
    
    //自旋锁
	spin_lock(&kthread_create_lock);
    //添加到kthread_create_list链表的尾部
	list_add_tail(&create->list, &kthread_create_list);
    //解锁
	spin_unlock(&kthread_create_lock);
    
    ///唤醒kthreadd进程,开启列表循环创建线程
	wake_up_process(kthreadd_task);

/*
	 * Wait for completion in killable state, for I might be chosen by
	 * the OOM killer while kthreadd is trying to allocate memory for
	 * new kernel thread.
	 */
	if (unlikely(wait_for_completion_killable(&done))) {
		/*
		 * If I was SIGKILLed before kthreadd (or new kernel thread)
		 * calls complete(), leave the cleanup of this structure to
		 * that thread.
		 */
		if (xchg(&create->done, NULL))
			return ERR_PTR(-EINTR);
		/*
		 * kthreadd (or new kernel thread) will call complete()
		 * shortly.
		 */
		wait_for_completion(&done);
	}
	task = create->result;

    if (!IS_ERR(task)) {
		static const struct sched_param param = { .sched_priority = 0 };

		vsnprintf(task->comm, sizeof(task->comm), namefmt, args);
		/*
		 * root may have changed our (kthreadd's) priority or CPU mask.
		 * The kernel thread should not inherit these properties.
		 */
        //task类型为task_struct,该函数作用是设置新线程调度策略,SCHED_NORMAL 普通调度策略,
        //非实时,优先级低于实时调度策略SCHED_FIFO和SCHED_RR,param的优先级上面定义为0
		sched_setscheduler_nocheck(task, SCHED_NORMAL, &param);
        //允许新线程在任意CPU上运行
		set_cpus_allowed_ptr(task, cpu_all_mask);
	}
	kfree(create);
	return task;
}

kthread_create()和kthread_run() ->  kthread_create_on_node ->  __kthread_create_on_node  -> list_add_tail 

这样会把创建的线程添加到 kthread_create_list 尾部,kthreadd会一直比对kthread_create_list链表,如果有将会创建线程。

6.小结

kthreadd 进程中会一直循环查找kthread_create_list链表,如果有新线程,会创建。如果没有 进入睡眠态,让出CPU

kthread_create()和kthread_run()Linux 内核创建线程调用__kthread_create_on_node  函数,会将新线程添加到kthread_create_list 尾部。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值