Linux-进程的管理与调度7(基于6.1内核)---Linux下2号进程
一、2号进程
内核初始化rest_init函数中,由进程 0 (swapper 进程)创建了两个process
-
init 进程 (pid = 1, ppid = 0)
-
kthreadd (pid = 2, ppid = 0)
所有其它的内核线程的ppid 都是 2,也就是说它们都是由kthreadd thread创建的
所有的内核线程在大部分时间里都处于阻塞状态(TASK_INTERRUPTIBLE)只有在系统满足进程需要的某种资源的情况下才会运行
它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程
二、2号进程的创建
在rest_init函数中创建2号进程的代码如下
pid = kernel_thread(kthreadd, NULL, 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);
三、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); // 允许kthreadd在任意CPU上运行
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;
/* 从链表中取得 kthread_create_info 结构的地址,在上文中已经完成插入操作(将
kthread_create_info 结构中的 list 成员加到链表中,此时根据成员 list 的偏移
获得 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;
}
kthreadd的核心是一for和while循环体。
在for循环中,如果发现kthread_create_list是一空链表,则调用schedule调度函数,因为此前已经将该进程的状态设置为TASK_INTERRUPTIBLE,所以schedule的调用将会使当前进程进入睡眠。
如果kthread_create_list不为空,则进入while循环,在该循环体中会遍历该kthread_create_list列表,对于该列表上的每一个entry,都会得到对应的类型为struct kthread_create_info的节点的指针create.
然后函数在kthread_create_list中删除create对应的列表entry,接下来以create指针为参数调用create_kthread(create).
create_kthread的过程如下:
四、create_kthread完成内核线程创建
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).
其实就是调用首先构造一个假的上下文执行环境,最后调用 do_fork()
返回进程 id, 创建后的线程执行 kthread 函数
*/
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);
kfree(create->full_name);
if (!done) {
kfree(create);
return;
}
create->result = ERR_PTR(pid);
complete(done);
}
}
在create_kthread()函数中,会调用kernel_thread来生成一个新的进程,该进程的内核函数为kthread,调用参数为
pid = kernel_thread(kthread, create, CLONE_FS | CLONE_FILES | SIGCHLD);
创建的内核线程执行的事件kthread
此时回到 kthreadd thread,它在完成了进程的创建后继续循环,检查 kthread_create_list 链表,如果为空,则 kthreadd 内核线程休眠过去。
在内核中通过kernel_create或者其他方式创建一个内核线程, 然后kthreadd内核线程被唤醒, 来执行内核线程创建的真正工作,于是这里有三个线程:
1、kthreadd已经光荣完成使命(接手执行真正的创建工作),睡眠。
2、唤醒kthreadd的线程由于新创建的线程还没有创建完毕而继续睡眠 (在 kthread_create函数中)。
3、新创建的线程已经正在运行kthread,但是由于还有其它工作没有做所以还没有最终创建完成。
五、新创建的内核线程kthread函数
static int kthread(void *_create)
{
static const struct sched_param param = { .sched_priority = 0 };
/* 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 = to_kthread(current);
/* Release the structure when caller killed by a fatal signal. */
done = xchg(&create->done, NULL);
if (!done) {
kfree(create->full_name);
kfree(create);
kthread_exit(-EINTR);
}
self->full_name = create->full_name;
self->threadfn = threadfn;
self->data = data;
/*
* The new thread inherited kthreadd's priority and CPU mask. Reset
* back to default in case they have been changed.
*/
sched_setscheduler_nocheck(current, SCHED_NORMAL, ¶m);
set_cpus_allowed_ptr(current, housekeeping_cpumask(HK_TYPE_KTHREAD));
/* OK, tell user we're spawned, wait for stop or wakeup */
设置运行状态为 TASK_UNINTERRUPTIBLE */
__set_current_state(TASK_UNINTERRUPTIBLE);
create->result = current;
/*
* Thread is going to call schedule(), do not preempt it,
* or the creator may spend more time in wait_task_inactive().
*/
/* current 表示当前新创建的 thread 的 task_struct 结构 */
preempt_disable();
complete(done);
/* 至此线程创建完毕 , 执行任务切换,让出 CPU */
schedule_preempt_disabled();
preempt_enable();
ret = -EINTR;
if (!test_bit(KTHREAD_SHOULD_STOP, &self->flags)) {
cgroup_kthread_ready();
__kthread_parkme(self);
ret = threadfn(data);
}
kthread_exit(ret);
}
线程创建完毕:
创建新 thread 的进程恢复运行 kthread_create() 并且返回新创建线程的任务描述符,新创建的线程由于执行了 schedule() 调度,此时并没有执行。直到使用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);
}
kthread_exit(ret);
六、总结
kthreadd进程由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理,它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程。
在内核中通过kernel_create或者其他方式创建一个内核线程, 然后kthreadd内核线程被唤醒, 来执行内核线程创建的真正工作,新的线程将执行kthread函数, 完成创建工作,创建完毕后让出CPU,因此新的内核线程不会立刻运行.需要手工 wake up, 被唤醒后将执行的真正工作函数:
1、任何一个内核线程入口都是 kthread()。
2、通过 kthread_create() 创建的内核线程不会立刻运行.需要手工 wake up。
3、通过 kthread_create() 创建的内核线程有可能不会执行相应线程函数threadfn而直接退出。
98

被折叠的 条评论
为什么被折叠?



