上一篇<<linux内核启动过程3:内核初始化阶段>>分析到了start_kernel执行流程,本篇继续内核切换到运行时状态。

内核运行时状态

内核初始化流程已经分析完成,如何保持内核进入运行时状态(不退出),接下来分析跳过的函数arch_call_rest_init:

/* Do the rest non-__init'ed, we're now alive */arch_call_rest_init();

进入arch_call_rest_init函数:

     rest_init();...struct task_struct *tsk;int pid;rcu_scheduler_starting();rcu_scheduler_active = RCU_SCHEDULER_RUNNING;/*#define RCU_SCHEDULER_INACTIVE  0#define RCU_SCHEDULER_INIT      1#define RCU_SCHEDULER_RUNNING   2
*/

rcu_scheduler_active设置为运行标志

     /** 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.*/pid = kernel_thread(kernel_init, NULL, CLONE_FS);/* kernel_init ---- 设置内核抢占模式(如mm、rt、dl等初始化),打开/dev/console,在释放内存之前,需要完成所有异步初始化代码,运行"/init"进程,运行/sbin/init、/etc/init、/bin/init、/bin/sh *//* system_state = SYSTEM_RUNNING; */

kernel_init这里最终运行/init、/sbin/init、/etc/init、/bin/init、/bin/sh(systemd安装包内,systemd --switched-root --system… 为(pid)1进程,用户空间进程基于父进程1),进入kernel_thread函数:

/** Create a kernel thread.*/
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{struct kernel_clone_args args = {.flags          = ((lower_32_bits(flags) | CLONE_VM |CLONE_UNTRACED) & ~CSIGNAL),.exit_signal    = (lower_32_bits(flags) & CSIGNAL),.stack          = (unsigned long)fn,.stack_size     = (unsigned long)arg,};return _do_fork(&args);
}/*struct pid init_struct_pid = {.count          = REFCOUNT_INIT(1),.tasks          = {{ .first = NULL },{ .first = NULL },{ .first = NULL },},              .level          = 0, .numbers        = { {.nr             = 0,.ns             = &init_pid_ns,}, }};
*/

kernel_init这里创建的是0(pid)进程,从.numbers[0].nr得到init(pid)

     rcu_read_lock(); /* 主要执行禁止内核抢占 + raw_spin_lock,进入无填充(CD=1,NW=0)缓存模式并刷新缓存 ... cr0 = read_cr0() | X86_CR0_CD; write_cr0(cr0); ... */tsk = find_task_by_pid_ns(pid, &init_pid_ns); /* 通过pid找到task_struct结构对象,通过idr机制 */set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id())); /* 启动CPU上的引脚初始化,用于初始化的CPU到非隔离CPU */rcu_read_unlock();/*  恢复抢占模式,取消读锁

找到通过pid找到task_struct结构对象,初始化CPU到非隔离CPU

     numa_default_policy();pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);//创建kthreadd,pid为2,内核层线程继承与它(它的子级共享进程)rcu_read_lock();kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);rcu_read_unlock();

创建kthreadd,pid为2,内核层相关线程继承与它

     system_state = SYSTEM_SCHEDULING;complete(&kthreadd_done);/** The boot idle thread must execute schedule()* at least once to get things moving:*/schedule_preempt_disabled();/* Call into cpu_idle with preempt disabled */cpu_startup_entry(CPUHP_ONLINE);

kthreadd在内核运行期间不退出,进入cpu_startup_entry函数:

void cpu_startup_entry(enum cpuhp_state state)
{arch_cpu_idle_prepare();cpuhp_online_idle(state);while (1)do_idle();
}

进入do_idle函数:

static void do_idle(void)
{int cpu = smp_processor_id();/** If the arch has a polling bit, we maintain an invariant:** Our polling bit is clear if we're not scheduled (i.e. if rq->curr !=* rq->idle). This means that, if rq->idle has the polling bit set,* then setting need_resched is guaranteed to cause the CPU to* reschedule.*/// 只有所有进程空闲时,才可能调用到idle进程(pid 0)__current_set_polling();tick_nohz_idle_enter();while (!need_resched()) {  rmb();local_irq_disable();if (cpu_is_offline(cpu)) {tick_nohz_idle_stop_tick();cpuhp_report_idle_dead();arch_cpu_idle_dead();}arch_cpu_idle_enter();/** In poll mode we reenable interrupts and spin. Also if we* detected in the wakeup from idle path that the tick* broadcast device expired for us, we don't want to go deep* idle as we know that the IPI is going to arrive right away.*/if (cpu_idle_force_poll || tick_check_broadcast_expired()) {tick_nohz_idle_restart_tick();cpu_idle_poll();} else {cpuidle_idle_call();}arch_cpu_idle_exit();}/** Since we fell out of the loop above, we know TIF_NEED_RESCHED must* be set, propagate it into PREEMPT_NEED_RESCHED.** This is required because for polling idle loops we will not have had* an IPI to fold the state for us.*/preempt_set_need_resched();tick_nohz_idle_exit();__current_clr_polling();/** We promise to call sched_ttwu_pending() and reschedule if* need_resched() is set while polling is set. That means that clearing* polling needs to be visible before doing these things.*/smp_mb__after_atomic();/** RCU relies on this call to be done outside of an RCU read-side* critical section.*/flush_smp_call_function_from_idle();schedule_idle();if (unlikely(klp_patch_pending(current)))klp_update_patch_state(current);
}

进入schedule_idle函数:

void __sched schedule_idle(void)
{/** As this skips calling sched_submit_work(), which the idle task does* regardless because that function is a nop when the task is in a* TASK_RUNNING state, make sure this isn't used someplace that the* current task can be in any other state. Note, idle is always in the* TASK_RUNNING state.*/WARN_ON_ONCE(current->state);do {__schedule(false);} while (need_resched());
}

在__schedule完成可执行进程到下一个可执行进程的切换,返回用户空间,cond_resched()从系统调用或异常返回到用户空间,schedule()从中断处理程序返回用户空间。

arch_call_rest_init函数初始化后:
    1. 启动了(pid)0进程,执行/init启动应用服务(pid)1进程systemd(应用层父进程,systemd安装包中分析);
    2. 启动(pid)2进程kthreadd(内核层(线程)的父进程)。

其中(pid)0进程属于(pid)1进程和(pid)2进程的父进程,(pid)1进程为应用空间父进程,(pid)2进程为内核空间父进程。

linux内核启动过程4:内核运行时相关推荐

  1. linux文件系统启动流程,linux 内核启动过程以及挂载android 根文件系统的过程

    转载 作者:汕头大学-黄珠唐 时间:2009 年10 月29 日 主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源 ...

  2. linux启动过程中内核拷贝,轻松识破linux内核启动过程中的“”套路“”

    内核启动流程相关的内容让很多热爱linux的小伙伴既爱又恨,因为这是了解linux系统基本构造的良好过程同时由于其本身复杂且底层,脑子中的脉络不是很清晰,本文就总结了一些优秀博文,以自己的理解来解构一 ...

  3. Linux内核移植之四:内核启动过程

    内容来自 韦东山<嵌入式Linux应用开发完全手册> 与移植U-Boot的过程相似,在移植Linux之前,先了解它的启动过程.Linux的启动过程可以分为两部分:架构/开发板相关的引导过程 ...

  4. Linux内核启动过程概述

    Hi!大家好,我是CrazyCatJack.今天给大家带来的是Linux内核启动过程概述.希望能够帮助大家更好的理解Linux内核的启动,并且创造出自己的内核^_^ Linux的启动代码真的挺大,从汇 ...

  5. Linux移植之内核启动过程引导阶段分析

    在Linux移植之make uImage编译过程分析中已经提到了uImage是一个压缩的包并且内含压缩程序,可以进行自解压.自解压完成之后内核代码从物理地址为0x30008000处开始运行.下面分析在 ...

  6. 简述arm linux内核启动流程,Linux内核启动过程和Bootloader(总述)

    1.Linux内核启动过程概述 一个嵌入式 Linux 系统从软件角度看可以分为四个部分:引导加载程序(Bootloader),Linux 内核,文件系统,应用程序.其中 Bootloader是系统启 ...

  7. linux内核启动过程5:启动用户空间

    上一篇<<linux内核启动过程4:内核运行时>>分析到了内核进入运行时状态(不退出),本篇分析用户空间(用户层)的加载过程. 启动应用空间 进入kernel_init函数,在 ...

  8. linux内核启动过程2:保护模式执行流程

    上一篇<<linux内核压缩制作bzImage>>分析了bzImage制作流程,本篇继续分析内核启动过程,从实模式跳转到保护模式及后续执行流程. protected_mode_ ...

  9. Linux内核启动过程和Bootloader(总述)

    1.Linux内核启动过程概述     一个嵌入式 Linux 系统从软件角度看可以分为四个部分:引导加载程序(Bootloader),Linux 内核,文件系统,应用程序.其中 Bootloader ...

最新文章

  1. 在线作图|在线做随机森林分析
  2. 全文检索4.5查询语法
  3. 分割BiSeNet笔记
  4. cocos2d-x 输出debug信息
  5. arm linux 显示屏 10钟黑屏
  6. JAVA中dis_求助!!为什么我的dispose()不起作用
  7. 全球及中国绿色建筑产业规模现状与未来走势分析报告2022版
  8. java 继承与派生4.6-4.7 2020.3.27
  9. Redis入门与数据类型介绍
  10. 1.编写程序,打印基本ASCII码表(可印刷32到126)。
  11. 2015国内征信机构大数据整合能力对比
  12. 前端学习(1553):复习2
  13. red flag linux指定域名,Red Flag Server 4.1 系统管理手册(适用桌面linux4.1) 6
  14. win10下安装OpenAI Gym
  15. open SUSE 查看本机ip地址
  16. 《图解网络硬件》网络硬件通用基础知识
  17. 向安装包中添加设备 UDID. 蒲公英内测
  18. KUKA库卡机器人零点失效维修案例
  19. 玲珑学院OJ 1130 - 喵哈哈村的魔法大师╳灬兲笙疯癫°月【强连通+可相交最小路径覆盖+背包】
  20. 电脑PHP动画制作画板,HTML_html5教程制作简单画板代码分享,HTML5制作简单画板 复制代码代 - phpStudy...

热门文章

  1. 《新版阿里巴巴Java开发手册》提到的三目运算符的空指针问题到底是个怎么回事?
  2. 今天,我收到了蚂蚁金服offer
  3. Linux 内核维护缺后浪,前浪Linus缺点Fun
  4. 开发指南专题二:JEECG微云快速开发平台JEECG框架初探
  5. ElementUI + express实现头像上传及后台图片保存
  6. Kubernetes中使用CronJob定时备份etcd集群数据
  7. 紫书 习题8-14 UVa 1616(二分+小数化分数+精度)
  8. IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理
  9. PCL中使用FLANN库(2)
  10. pyCharm最新2017激活