3.7.2.4. kthreadd
内核初始化 rest_init
函数中,由进程0创建了两个process
init进程(pid = 1, ppid = 0)
kthreadd(pid = 2, ppid = 0)
所有其他内核线程的ppid都是2,也就是说他们都是由kthreadd创建的
3.7.2.4.1. 2号进程的创建
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();
/*
* Enable might_sleep() and smp_processor_id() checks.
* They cannot be enabled earlier because with CONFIG_PREEMPTION=y
* kernel_thread() would trigger might_sleep() splats. With
* CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled
* already, but it's stuck on the kthreadd_done completion.
*/
system_state = SYSTEM_SCHEDULING;
complete(&kthreadd_done);
3.7.2.4.2. 2号进程的循环
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在任意cpuh上运行
set_mems_allowed(node_states[N_MEMORY]);
current->flags |= PF_NOFREEZE;
cgroup_init_kthreadd();
for (;;) {
//首先将线程状态设置为TASK_INTERRUPTIBLE,如果当前没有要创建的线程则主动放弃CPU,此进程变为阻塞态
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list))
schedule();
//没有要创建的内核线程,什么也不做执行一次调度,让出CPU
//运行到此处表示Kthreadd线程被唤醒,设置进程状态为TASK_RUNNING
__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);
//从链表中删除
spin_unlock(&kthread_create_lock);
create_kthread(create);
//创建线程
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
在create_kthread()函数中,会调用kernel_thread来生成一个新的进程
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
kernel_thread会调用 _do_fork
函数
/*
* Create a kernel thread.
*/
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct kernel_clone_args args = {
.flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL),
.exit_signal = (flags & CSIGNAL),
.stack = (unsigned long)fn,
.stack_size = (unsigned long)arg,
};
return _do_fork(&args);
}
新创建的内核线程kthread函数
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 completion *done;
struct kthread *self;
int ret;
self = kzalloc(sizeof(*self), GFP_KERNEL);
set_kthread_struct(self);
/* If user was SIGKILLed, I release the structure. */
done = xchg(&create->done, NULL);
if (!done) {
kfree(create);
do_exit(-EINTR);
}
if (!self) {
create->result = ERR_PTR(-ENOMEM);
complete(done);
do_exit(-ENOMEM);
}
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(done);
schedule();
//执行任务切换,让出CPU
//执行到此处时说明内核调用了wake_up_process(p)唤醒了新创建的线程,线程被唤醒后会接着执行threadfn(data)
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
cgroup_kthread_ready();
__kthread_parkme(self);
ret = threadfn(data);
}
do_exit(ret);
}
总结
kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间,负责所有内核线程的调度和管理,他的任务就是管理和调度其他内核线程kernel_thread
我们在内核中通过kernel_create或者其他方式创建一个内核线程,然后kthreadd内核线程会被唤醒,来执行真正的内核线程创建工作,新的线程将执行kthread函数,完成创建工作. 创建完毕后让出CPU,因此新的内核线程不会立刻运行,需要手动wake up被唤醒后将执行自己的真正工作函数
任何一个内核线程入口都是kthread()
通过kthread_create()创建的内核线程不会立刻运行,需要手动wake up
通过kthread_create()创建的内核线程有可能不会执行相应的线程函数threadfn而直接退出