设为首页 加入收藏

TOP

Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)【转】(四)
2019-09-01 23:10:00 】 浏览:91
Tags:Linux 进程 init_task 今生 idle ----Linux 管理 调度
init进程应该是一个用户空间的进程, 但是这里却是通过kernel_thread的方式创建的, 哪岂不是式一个永远运行在内核态的内核线程么, 它是怎么演变为真正意义上用户空间的init进程的?

1号kernel_init进程完成linux的各项配置(包括启动AP)后,就会在/sbin,/etc,/bin寻找init程序来运行。该init程序会替换kernel_init进程(注意:并不是创建一个新的进程来运行init程序,而是一次变身,使用sys_execve函数改变核心进程的正文段,将核心进程kernel_init转换成用户进程init),此时处于内核态的1号kernel_init进程将会转换为用户空间内的1号进程init。户进程init将根据/etc/inittab中提供的信息完成应用程序的初始化调用。然后init进程会执行/bin/sh产生shell界面提供给用户来与Linux系统进行交互。

调用init_post()创建用户模式1号进程。

关于init其他的信息我们这次先不研究,因为我们这篇旨在探究0号进程的详细过程,

创建kthreadd

在rest_init函数中,内核将通过下面的代码产生第一个kthreadd(pid=2)

pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);

它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthread的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程

0号进程演变为idle

    /*
    * 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=0的进程,在创建了init进程后,pid=0的进程调用 cpu_idle()演变成了idle进程。

0号进程首先执行init_idle_bootup_task, 让init_task进程隶属到idle调度类中。即选择idle的调度相关函数。

这个函数被定义在kernel/sched/core.c中,如下

void init_idle_bootup_task(struct task_struct *idle)
{
    idle->sched_class = &idle_sched_class;
}

接着通过schedule_preempt_disabled来执行调用schedule()函数切换当前进程,在调用该函数之前,Linux系统中只有两个进程,即0号进程init_task和1号进程kernel_init,其中kernel_init进程也是刚刚被创建的。调用该函数后,1号进程kernel_init将会运行

这个函数被定义在kernel/sched/core.c中,如下

/**
* schedule_preempt_disabled - called with preemption disabled
*
* Returns with preemption disabled. Note: preempt_count must be 1
*/
void __sched schedule_preempt_disabled(void)
{
    sched_preempt_enable_no_resched();
    schedule();
    preempt_disable();
}

最后cpu_startup_entry调用cpu_idle_loop(),0号线程进入idle函数的循环,在该循环中会周期性地检查

cpu_startup_entry定义在kernel/sched/idle.c

 void cpu_startup_entry(enum cpuhp_state state)
{
    /*
    * This #ifdef needs to die, but it's too late in the cycle to
    * make this generic (arm and sh have never invoked the canary
    * init for the non boot cpus!). Will be fixed in 3.11
    */
#ifdef CONFIG_X86
    /*
    * If we're the non-boot CPU, nothing set the stack canary up
    * for us. The boot CPU already has it initialized but no harm
    * in doing it again. This is a good place for updating it, as
    * we wont ever return from this function (so the invalid
    * canaries already on the stack wont ever trigger).
    */
    boot_init_stack_canary();
#endif
    arch_cpu_idle_prepare();
    cpu_idle_loop();
}

其中cpu_idle_loop就是idle进程的事件循环,定义在kernel/sched/idle.c

整个过程简单的说就是,原始进程(pid=0)创建init进程(pid=1),然后演化成idle进程(pid=0)。init进程为每个从处理器(运行队列)创建出一个idle进程(pid=0),然后演化成/sbin/init。

idle的运行与调度

idle的workload–cpu_idle_loop

从上面的分析我们知道,idle在系统没有其他就绪的进程可执行的时候才会被调度。不管是主处理器,还是从处理器,最后都是执行的cpu_idle_loop()函数

其中cpu_idle_loop就是idle进程的事件循环,定义在kernel/sched/idle.c,早期的版本中提供的是cpu_idle,但是这个函数是完全依赖于体系结构的,不利用架构的分层,因此在新的内核中更新为更加通用的cpu_idle_loop,由他来调用体系结构相关的代码

所以我们来看看cpu_idle_loop做了什么事情。

因为idle进程中并不执行什么有意义的任务,所以通常考虑的是两点

  1. 节能
  2. 低退出延迟。

其代码如下

/*
 * Generic idle loop implementation
 *
 * Called with polling cleared.
 */
static void cpu_idle_loop(void)
{
        while (1) {
                /*
                 * If the arch has a polling bit, we maintain an invariant:
                 *
                 * Our polling bit is clear if we're not scheduled (i
首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇高通平台如何使用QPST抓DUMP 下一篇进程调度

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目