Linux内核启动函数start_kernel的简单分析
2016-04-05 10:09
337 查看
欧长坤
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
这学期学校恰好有操作系统的课程,上个学习就开始寻思研究研究Linux内核代码,恰好MOOC有这个课程,遂选了此课。
一、准备工作
废话不多说,命令一行行敲下去,搭建好环境。
二、分析
在这个实验过程中,我们得先阅读以下源代码,才能够逐步设置断点。我们可以在linux-3.18.6/init/main.c这里找到start_kernel所在函数的源代码。
这个页面的好处是可以随时跟踪不用麻烦的寻找各个代码存放的位置。
因此,我们来看看start_kernel()的代码(删掉了一些我们不关心的多余的代码):
在start_kernel的最后一项初始化,就是有关内核进程管理的初始化了。一旦这一项初始化完成,内核就加载成功了。如果你看过我上一篇文章的分析,就能知道,在上一次我们自行编写的简单系统内核,实际上是在rest_init前插入了一段我们自己的函数my_start_kernel,插入这个函数之后,我们自己的内核通过PCB的进程管理单元来管理了我们依次创建的四个简单进程,并通过时间片轮转的方式进行了调度。那么在实际的linux内核代码中,rest_init()到底是干什么才使得我们需要在它之前执行my_start_kernel呢?原因就是rest_init实际上是linux内核初始化进程的函数。如果我们在它执行之前自行创建我们自己的进程,并且利用自己的调度算法来调度之后创建的进程,那么rest_init则永远不会被执行,因为在它执行之前,我们自己的进程已经在轮转调度不会结束了。
下面我们就来看看实际linux初始化进程的内核代码rest_init(删掉了不关心的部分):
在执行kernel_thread时,kernel_init作为将要执行的函数指针传入,进程ID会被置为1。所以在这里,kernel_init内核线程被创建,进程号为1。
在完成内核进程的创建后,会创建kthreadd内核线程,作用则是管理和调度其他的内核线程。
那么,我们会产生这样的疑惑:这里的for(;;)什么时候会被停止呢?怎么让CPU执行其他的代码呢?
容易注意到,在循环内部,当list_empty()进行判断时,如果为真,那么就会调用schedule(),而这个schedule会执行很多复杂庞大的调度,其核心任务就是为了让出CPU的使用权,因此,这个线程也没有必要关闭。限于篇幅,就不继续展开了,有机会在后面的博文里聊。
我们先回过头来,看看kernel_init。
kernel_init既然是将要执行,我们就来看看kernel_init又会执行什么:
注意,我们创建的进程ID实际上是从1开始的。其中在kernel_init中创建的是1号进程,在刚才的kthreadd中创建的是2号进程。
那么接下来,为了让系统能够运作起来,剩下的这三行代码完成了非常重要的工作,它完成了CPU对任务的调度初始化,让内核真正的开始进入用户主导的阶段:
然后通过schedule_preempt_disabled来设置这个进程是不会被调度。因为CPU显然利用率越高越好,不可能让调度程序调度一个只消耗时间片的进程。
最后,cpu_startup_entry 就会使得CPU在idle这样一个循环内进行工作,不断往复,从不返回。
三、实验过程
好,那么我们现在就来正式进行实验。我们来逐步加载idel进程和1号进程。
通过上面的分析,我们注意到有下面几个比较重要的断点需要我们设置:
start_kernel, page_address_init, trap_init, mm_init, rest_init, kernel_init, kthreadd, init_idle_bootup_task, cpu_startup_entry
下面是一些关键性的过程:
可以看到,其实在这里,内核的加载工作已经完成了,符合我们的预期。
以上就是对内核启动的一个跟踪了。
四、总结
我们来总结一下上面的全部分析过程:首先,几乎所有的内核模块均会在start_kernel进行初始化。在start_kernel中,会对各项硬件设备进行初始化,包括一些page_address、tick等等,直到最后需要执行的rest_init中,会开始让系统跑起来。
那么,rest_init这个过程中,会调用调用kernel_thread来创建内核线程kernel_init,它创建用户的init进程,初始化内核,并设置成1号进程,这个进程会继续做相关的系统初始化。
然后,start_kernel会调用kernel_thread并创建kthreadd,负责管理内核中得所有线程,然后进程ID会被设置为2。
最后,会创建idle进程(0号进程),不能被调度,并利用循环来不断调号空闲的CPU时间片,并且从不返回。当然,不同的内核版本对这个状态的描述会有所差异,至于这个进程能不能够被抢占,本文描述的内核版本为3.18.6,是可以的。如图所示:
相关文章推荐
- linux调整home分区和root分区大小
- linux 如何显示一个文件的某几行(中间几行)
- Linux服务之Centos6.3下利用open***部署远程***服务
- Review Board Installing on Linux
- Linux命令:screen
- Linux编译安装Darwin Streaming Server 6.0.3
- Linux备份工具大集合
- 在CentOS/RHEL上设置SSH免密码登录
- Linux下C编程实现---获取本机IP地址
- Linux压缩保留源文件的方法
- CentOS-6.3-i386-bin-DVD1.iso下载地址
- centos7 开机/etc/rc.local不执行命令的问题
- linux platform总线的相关总结
- Linux grep命令和正则表达式
- 微软和 Linux :真正的浪漫还是有毒的爱情?
- 我的linux学习之路:apt-get常用命令
- Linux开发环境搭建与使用——ubuntu更新设置
- Linux学习要点(有些东西可以借鉴)
- Linux学习路线魔法图
- Debian GNU/Linux 8.4“Jessie”与7.10“Wheezy”正式发布