设为首页 加入收藏

TOP

Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)【转】(二)
2019-09-01 23:10:00 】 浏览:88
Tags:Linux 进程 init_task 今生 idle ----Linux 管理 调度
union thread_union init_thread_union __init_task_data = { INIT_THREAD_INFO(init_task) };

我们可以发现init_task是用INIT_THREAD_INFO宏进行初始化的, 这个才是我们真正体系结构相关的部分, 他与init_thread_info定义在一起,被定义在/arch/对应体系/include/asm/thread_info.h中,以下为x86架构的定义

参见
http://lxr.free-electrons.com/source/arch/x86/include/asm/thread_info.h?v=4.5#L65

#define INIT_THREAD_INFO(tsk)                   \
{                                               \
    .task           = &tsk,                 \
    .flags          = 0,                    \
    .cpu            = 0,                    \
    .addr_limit     = KERNEL_DS,            \
}

其他体系结构的定义请参见
/arch/对应体系/include/asm/thread_info.h中

架构 定义
x86 arch/x86/include/asm/thread_info.h
arm64 arch/arm64/include/asm/thread_info.h

init_thread_info定义中的__init_task_data表明该内核栈所在的区域位于内核映像的init data区,我们可以通过编译完内核后所产生的System.map来看到该变量及其对应的逻辑地址

cat System.map-3.1.6 | grep init_thread_union

进程内存空间

init_task的虚拟地址空间,也采用同样的方法被定义

由于init_task是一个运行在内核空间的内核线程, 因此其虚地址段mm为NULL, 但是必要时他还是需要使用虚拟地址的,因此avtive_mm被设置为init_mm

参见
http://lxr.free-electrons.com/source/include/linux/init_task.h?v=4.5#L202

.mm             = NULL,                                         \
  .active_mm      = &init_mm,                                     \

其中init_mm被定义为init-mm.c中,参见 http://lxr.free-electrons.com/source/mm/init-mm.c?v=4.5#L16

struct mm_struct init_mm = {
    .mm_rb          = RB_ROOT,
    .pgd            = swapper_pg_dir,
    .mm_users       = ATOMIC_INIT(2),
    .mm_count       = ATOMIC_INIT(1),
    .mmap_sem       = __RWSEM_INITIALIZER(init_mm.mmap_sem),
    .page_table_lock =  __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
    .mmlist         = LIST_HEAD_INIT(init_mm.mmlist),
    INIT_MM_CONTEXT(init_mm)
};

0号进程演化

rest_init创建init进程(PID =1)和kthread进程(PID=2)

Linux在无进程概念的情况下将一直从初始化部分的代码执行到start_kernel,然后再到其最后一个函数调用rest_init

大致是在vmlinux的入口startup_32(head.S)中为pid号为0的原始进程设置了执行环境,然后原是进程开始执行start_kernel()完成Linux内核的初始化工作。包括初始化页表,初始化中断向量表,初始化系统时间等。

从rest_init开始,Linux开始产生进程,因为init_task是静态制造出来的,pid=0,它试图将从最早的汇编代码一直到start_kernel的执行都纳入到init_task进程上下文中。

这个函数其实是由0号进程执行的, 他就是在这个函数中, 创建了init进程和kthreadd进程

这部分代码如下:

static noinline void __init_refok rest_init(void)
{
    int pid;

    rcu_scheduler_starting();
    smpboot_thread_init();

    /*
    * 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);
    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);
}
  1. 调用kernel_thread()创建1号内核线程, 该线程随后转向用户空间, 演变为init进程
  2. 调用kernel_thread()创建kthreadd内核线程。
  3. init_idle_bootup_task():当前0号进程init_task最终会退化成idle进程,所以这里调用init_idle_bootup_task()函数,让init_task进程隶属到idle调度类中。即选择idle的调度相关函数。
  4. 调用schedule()函数切换当前进程,在调用该函数之前,Linux系统中只有两个进程,即0号进程init_task和1号进程kernel_init,其中kernel_init进程也是刚刚被创建的。调用该函数后,1号进程kernel_init将会运行!
  5. 调用cpu_idle(),0号线程进入idle函数的循环,在该循环中会周期性地检查。

创建kernel_init

在rest_init函数中,内核将通过下面的代码产生第一个真正的进程(pid=1):

kernel_thread(kernel_init, NULL, CLONE_FS);

这个进程就是着名的pid为1的init进程,它会继续完成剩下的初始化工作,然后execve(/sbin/init), 成为系统中的其他所有进程的祖先。

但是这里我们发现一个问题,

首页 上一页 1 2 3 4 5 下一页 尾页 2/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇高通平台如何使用QPST抓DUMP 下一篇进程调度

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目