2017-2018-1 20179209《Linux内核原理与分析》第四周作业
本周学习内容为《跟踪分析MenuOS简单linux系统的启动过程》和教材中的进程调度及内核数据结构。
一.跟踪分析Linux内核的启动过程
这个实验我是在实验楼环境中完成的,最初想在自己的环境中完成,可在make的时候出现了问题,原本需要很长时间的make过程只用了10几秒,而且报了错。经过我上网查资料发现可能是因为我的实验环境有问题(kali1-4.3.0)。所以我还是选择了实验楼,其实在实验楼上同样有问题,每次冻结程序进行调试时必然卡顿,然后崩溃。我怀疑是网速的问题,果然,换了流量之后流畅多了。
水平分割终端,上面用来输入命令,下面用来调试。启动内核并冻结,然后进入gdb模式,设置函数断点,使启动过程断在start_kernel
处:
__init start_kernel(void)
是最低限度的初始化系统。这里面的每一个函数都有其存在的意义,光本次实验的这个小型系统,它的初始化队列中就大概涵盖200多个函数,可想linux系统内核的庞大。
这里我萌生了一个想法,我们都知道,启动linux后会出现一行一行的加载,我想是不是就跟start_kernel
中的函数有关,所以打算验证一下这个猜测。
从上图大家可以看到,我在509行也就是lockdep_init();
函数处设置断点,继续运行后虚拟机中的启动过程并没有任何变化。
紧接着,我又在511行(smp_setup_processor_id();)处设置断点,这里面的很多函数光靠名字很难猜出它对linux内核启动有什么样的作用,但这对实验没有太大的影响,因为它们的意义不是重点。运行后还是没有任何变化。如下图:
两次跳跃大概10几个函数,竟然没有变化,所以下一个断点,我选择了在528行boot_cpu_init();
又是boot,又是cpu的,应该很重要,即使初始化也应该先进行检查吧,但是仍然没有任何反应:
断535setup_per_cpu_areas();
依旧:
trap_init();
这是对中断机制的初始化,出于好奇,我s进了这个函数:
出发点是好的,但结果总不尽人意,这里面的大部分内容我是不明白的。这时候我想退出这个函数了:
从上图可以看出,程序的执行终于跳了出来,回到了start_kernel中。
其实经过这么多次的断,我已经有点怀疑我的猜测了,有可能这些系统初始化的过程并不对应那些启动画面中的一条一条的检查。所以我直接断了619行(跨度将近60行):
它终于出现了变化,证明了我的想法一样,返回去看619行是哪个函数的执行:
可能最初的变化并不是在locking_selftest();
函数,但肯定是在这60行中某一个函数开始。
最后一个断点设置在了start_kernel
中的最后一个函数rest_init()
,而且我也跳进去看了看:
finish后,start_kernel()
结束:
start_kernel函数相当于c程序中的main函数,我们都知道而且也这样做:在main函数中调用其他函数,而不直接把功能函数的实体写在main中;start_kernel一样,它的作用就是调用linux内核启动所需要的各类初始化。对于它里面这些函数的功能,见start_kernel函数分析。说实话,我觉得这个实验给我收获最大的不是对内核启动过程的分析,因为从最开始明白start_kernel的用途以及跳进初始化中断的那个函数开始,我就已经领悟到了内核启动的过程——对系统启动所需要的软硬件的初始化(这里的软硬件包括系统运行所需要的栈、内存、中断等),以及对0号和1号进程的启动。我想说的最大的收获是对gdb的理解,尽管在调试myod的时候就已经很熟练对gdb的使用,但经过对内核启动过程的分析,我发现其实gdb的功能远不止设置断点继续运行这么简单,所以我就上网查了查,发现这篇博客解释得比较完美。
二.教材内容总结
其实本科所学的操作系统这门课已经详解了进程调度的原理和手段,而且链表作为数据结构的一种,也早已实现过多次,所以这里我不再做过多陈述。只是总结一下,我认为的新的、或者原来没学的内容。
- Linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法。这种模块化结构被称为调度器类,它允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程。每个调度器都有一个优先级,基础的调度器代码定义在kernel/sched.c文件中,它会按照优先级顺序遍历调度类,拥有一个可执行进程的最高优先级的调度器类胜出,去选择下面要执行的那一个程序。这里我觉得调度器只存在于Linux操作系统中,Windows系统中没有。
- 红黑树是一种自平衡的二叉树,与普通二叉树相比,它具有着色属性,或红色或黑色。它通过以下六个属性来维持半平衡二叉树:
- 所有的节点不是红色,就是黑色
- 叶子节点都是黑色
- 叶子节点不包含数据
- 所有非叶子节点都有两个子节点
- 如果一个节点是红色,则它的子节点都是黑色
- 在一个节点到其叶子节点的路径中,如果总是包含同样数目的黑色节点,则该路径相比其他路径是最短的。 - 进程调度的主要入口点是函数schedule();其实在之前的《构建一个简单的Linux系统》实验中出现过,这个函数中嵌入汇编代码,实现进程的切换。
- 其实在进程切换和调度中,还有一个很重要的概念就是加锁机制。由于系统资源的有限,为避免产生死锁,必须对临界区资源加锁。想到这一概念我特意翻了一下课本,发现在内核同步那一章有讲。
总结:
可能由于之前操作系统的学习,让我的思想有些固化,对于进程调度和管理这些概念性的内容,即使再看一遍也提不出什么实质性的问题,我除了对内核代码感兴趣之外,其他还是希望观看其他学生提出的问题,希望能帮助他们解决;即使解决不了,那也可以来当做自己的问题处理。相比抽象地学习理论,我还是希望多些代码方面的练习,当然也不是直接像Linux内核这样庞大的代码,起初看到start_kernel 函数时确实吓了一大跳,如果要是在这门课结束之前,我弄不明白这么多代码该怎么办?而且这还只是内核中一部分;而且这个函数中的每个函数还有具体的实现。这样想想,似乎我连入门都没有。还是希望老师多讲解一下我们对这门课应该有的整体把握和方向。