通过从代码层面分析Linux内核启动来探知操作系统的启动过程

前言说明

本篇为网易云课堂Linux内核分析课程的第三周作业,我将围绕Linux 3.18的内核中的start_kernelinit进程启动过程来深入探知操作系统的启动,文中的代码来自Linux Kernel Organization3.18.9内核源码

本篇关键词:init进程idle进程Linux内核启动

分析

分析说明

  • 分析过程将把主要精力放在关键代码的分析上,代码分析的方式我是采用注释说明的方法,这样比较简洁直观,对于一些关键过程我会在代码后面采用图文说明的方式。
  • 文中我已经将3.18内核代码编译,而且加入debug调试信息,由于这里的过程在课堂上已经有详细讲解,这个过程我就不在赘述。

/* star_kernel是linux内核入口 */
asmlinkage __visible void __init start_kernel(void)
{char *command_line;char *after_dashes;/** Need to run as early as possible, to initialize the* lockdep hash:*/lockdep_init();set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();...boot_cpu_init();page_address_init();pr_notice("%s", linux_banner);setup_arch(&command_line);mm_init_cpumask(&init_mm);setup_command_line(command_line);setup_nr_cpu_ids();setup_per_cpu_areas();smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */...build_all_zonelists(NULL, NULL);page_alloc_init();...setup_log_buf(0);pidhash_init();vfs_caches_init_early();sort_main_extable();trap_init();mm_init();.../* Do the rest non-__init'ed, we're now alive */rest_init();
}
  • init/main.c
  • 以上为start_kernel函数的一个代码片段,在该函数之前的执行都是汇编,以C语言程序的思维来看,start_kernel就是整个Linux内核的“main”函数,即整个Linux内核的入口函数,在start_kernel中,开始有一个set_task_stack_end_magic(&init_task),这个函数中间的形参init_task,通过寻找在init/init_task.h中找到了struct task_struct init_task = INIT_TASK(init_task)(struct task中保存进程的相关信息,类似PCB),经过初始化init_task后,静态构造进程,这是Linux第一次拥有了进程,这就是后来的idle进程(pid为0),start_kernel之前的汇编代码到start_kernel执行,这里都会纳入idle进程的上下文(之前的汇编代码就是为了idle进程的执行做准备)。
  • 最后rest_init()标志着Linux内核初始化完成,在rest_init()中开始产生第一个真正意义上的进程,也就是init进程(即进程号为1的进程,其他所有用户进程的祖先进程),接下来就对rest_init()部分做详细分析

static noinline void __init_refok rest_init(void)
{int pid;rcu_scheduler_starting();/** 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);
}
  • kernel/fork
  • kernel_thread(kernel_init, NULL, CLONE_FS),这里通过这个函数创建了init进程,该函数具体代码如下:
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,(unsigned long)arg, NULL, NULL);
}

第一次参数为注册一个回调函数,kernel_init这个回调函数,do_fork是创建一个新的进程, 在此之中会为创建init进程进行各种工作,如初始化运行堆栈,调用相应的回掉函数等,通过回调kernel_init可以创建init进程,接下来具体分析下kernel_init


static int __ref kernel_init(void *unused)
{int ret;kernel_init_freeable();/* need to finish all async __init code before freeing the memory */async_synchronize_full();free_initmem();mark_rodata_ro();system_state = SYSTEM_RUNNING;numa_default_policy();flush_delayed_fput();if (ramdisk_execute_command) {ret = run_init_process(ramdisk_execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d)\n",ramdisk_execute_command, ret);}/** We try each of these until one succeeds.** The Bourne shell can be used instead of init if we are* trying to recover a really broken machine.*/if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;pr_err("Failed to execute %s (error %d).  Attempting defaults...\n",execute_command, ret);}if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;panic("No working init found.  Try passing init= option to kernel. ""See Linux Documentation/init.txt for guidance.");
}
  • init/main.c
  • kernel_init中我们重点关注以下代码,在这段代码中实际上是通过run_init_process来执行/sbin/init,通过中断向量0x80(system_call)来从内核发起系统调用,如果/sbin/init调用失败,则会继续调用接下来的文件/etc/init,/bin/init,/bin/sh
...
if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;
...

接下来我们回到rest_init的代码片段,rest_init执行完后,idle进程已经结束了他的使命,开始成为一个真正的idle进程,即真正的空闲进程,从这里开始内核的初始化真正结束了,用户态的阶段开始了

//rest_init
...省略
/** 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);
  • 这里的cpu_startup_entry(CPUHP_ONLINE)中的代码片段
void cpu_startup_entry(enum cpuhp_state state)
{...代码省略arch_cpu_idle_prepare();cpu_idle_loop();
}static void cpu_idle_loop(void)
{while (1) {...代码省略__current_set_polling();tick_nohz_idle_enter();...arch_cpu_idle_enter();arch_cpu_idle_exit();...schedule_preempt_disabled();...
  • 我们在这里看到一个cpu_idle_loop(),这里其中是一个死循环,而且从中可以看到CPU不断地进入idle状态不断的推出idle状态。
  • 从这里我们可以得到一个这样的结果并总结,idle进程是一个内核态进程,init进程是Linux系统刚刚开始有了进程概念下的进程,其最后会进行从内核态转向用户态,idle进程在内核初始化的时候的工作就是创建init进程。

我对Linux系统启动过程的一点理解

首先,启动计算机,载入汇编代码,直到start_kernel执行的这个阶段,idle进程(0号进程)就是从这个时间段产生的,这个阶段为idle执行上下文做准备,start_kernelinit_task就是idle进程(此时还没有Linux进程,仅仅是模拟的一个进程),然后在rest_init初始化并产生init进程(1号进程),整个操作系统开始从内核态向用户态转换。

截图

署名信息

吴欣伟 原创作品转载请注明出处 《Linux内核分析》MOOC课程: http://mooc.study.163.com/course/USTC-1000029000![](https://images0.cnblogs.com/blog2015/363679/201503/222205206093254.jpg)

转载于:https://www.cnblogs.com/MarkWoo/p/4358128.html

通过从代码层面分析Linux内核启动来探知操作系统的启动过程相关推荐

  1. 实验三 Linux的启动与关闭,实验三:跟踪分析Linux内核的启动过程

    Ubuntu 16.04下搭建MenuOS的过程: 1.下载内核源代码编译内核 1 # 下载内核源代码编译内核 2 cd ~/LinuxKernel/ 3 wget https://www.kerne ...

  2. 实验三:跟踪分析Linux内核的启动过程

    Ubuntu 16.04下搭建MenuOS的过程: 1.下载内核源代码编译内核 1 # 下载内核源代码编译内核2 cd ~/LinuxKernel/3 wget https://www.kernel. ...

  3. linux内核启动分析 三,Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  4. linux内核烧制,学会分析Linux内核需要多久?8分钟

    Linux的最大的好处之一就是它的源码公开.同时,公开的核心源码也吸引着无数的电脑爱好者和程序员:他们把解读和分析Linux的核心源码作为自己的最大兴趣,把修改Linux源码和改造Linux系统作为自 ...

  5. fork的黑科技,它到底做了个啥,源码级分析linux内核的内存管理

    最近一直在学习linux内核源码,总结一下 https://github.com/xiaozhang8tuo/linux-kernel-0.11 一份带注释的源码,学习用. fork的黑科技,它到底做 ...

  6. linux内核分析设备,手把手教你分析Linux内核

    Linux的最大的好处之一就是它的源码公开.同时,公开的核心源码也吸引着无数的电脑爱好者和程序员:他们把解读和分析Linux的核心源码作为自己的最大兴趣,把修改Linux源码和改造Linux系统作为自 ...

  7. 跟踪分析Linux内核5.0系统调用处理过程

    跟踪分析Linux内核5.0系统调用处理过程 学号384 原创作业转载请注明出处+中国科学技术大学孟宁老师的Linux操作系统分析 https://github.com/mengning/linuxk ...

  8. Linux内核源代码分析——Linux内核的入口

    Jack:hi,淫龙,在Linux内核的源代码里,有几段汇编代码,那几段代码是负责Linux内核引导的. 我:是的.早期的Linux内核引导代码只有bootsect.s.setup.s.head.s这 ...

  9. 一文分析Linux内核klist链表

    1.klist链表相关结构 内核源码中,klist相关的头文件是include/linux/klist.h,实现的文件是lib/klist.c中,接下来分析klist链表头和klist链表节点的定义: ...

最新文章

  1. Mac 安装多个版本jdk
  2. 对于初学者Python开发难学吗?适合初学者吗?
  3. C/S和B/S的区别
  4. a标签右侧尖括号_没想到贴线缆标签有很多讲究和技巧?一般人真不知道
  5. 【Gym - 101775J】Straight Master(差分,思维)
  6. 仅使用numpy从头开始实现神经网络,包括反向传播公式推导过程
  7. php分布式缓存系统 Memcached 入门
  8. SQLServer中Case when的一个意外问题
  9. 运用提示原则证明线性无关
  10. 为什么使用3msip2协议_TCP协议专场
  11. 怎么用电脑把优酷的kux格式转换成mp4格式
  12. AHT20温湿度传感器数据采集
  13. Matlab中zeros和ones函数用法
  14. SSL 1653 数字游戏
  15. clustalw序列比对_序列比较中ClustalW和BLAST的区别
  16. 移动端ui设计,移动端界面
  17. 4星+|《赋能:打造应对不确定性的敏捷团队》:海豹突击队学习伊拉克“基地”组织的组织形式并且最终战胜对方的故事...
  18. 屏蔽百度搜索结果中的广告
  19. Vue开发环境搭建和vue-cli脚手架
  20. 线性代数之向量间线性关系

热门文章

  1. 20 求图的割点和割边—Tarjan算法
  2. 【PMP考试秘笈】第一式:PMBOK中的输入输出工具规律
  3. Codeforces Round #296 (Div. 2) E. Data Center Drama (欧拉路)
  4. MDS100-16-ASEMI工业焊机、100A整流桥
  5. 项目经理在项目中的职责有哪些
  6. 统一信用代码n开头_加工中心代码小数点问题 斜杠作用 GOTO意思
  7. 论文《Cross-Session Aware Temporal Convolututional Network for Session-based Recommendation》阅读
  8. vegas使用技巧——vegas如何导入音频视频素材?
  9. 互动媒体技术——Processing模仿并拓展动态爱心图案
  10. 【网络基础】RIP基础概念