上一篇文章中分析了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, ¶m);
//允许新线程在任意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 尾部。