前言

 

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而直接退出