Linux 的初始化与启动过程

我们运行程序只需要点击应用程序的图标就可以了,但在这之前,我们必须启动我们的系统。在一切之前,我们必须有某些程序去引导我们系统的内核,这些程序就是内核引导程序了,例如LILO、GRUB、U-Boot、RedBoot。而这些引导程序同样需要被其他程序加载和运行,这样说下去,茫茫人生何处才是尽头啊?想必大家可以想到的----硬件!这么长的过程复杂、崎岖!正所谓万事开头难,但不怕,我们来一起走过去吧!

X86的引导过程如图:

Linux 的初始化与启动过程

cpu自身的初始化:这是引导的第一步,如果在多处理器系统上,那么每个cpu都要自身初始化。cpu初始化后,cpu从某个固定的位置(应该是0Xfffffff0)取指,这条指令是跳转指令,目的地是BIOS的首部代码,但是cpu并不在乎BIOS是否存在,它仅仅只是执行这个地址的指令而已!

BIOS:BIOS是只读存储器(ROM),被固化于主板上。其工作主要有两个,就是上图的加电自检即是POST(post on self  test)与加载内核引导程序。

那么他们是具体完成什么工作的呢?

1) 加电自检:完成系统的硬件检测,其中包括内存检测、系统总线检测等工作。

2) 加载内核引导程序:在POST完成后,就要加载内核引导程序了,那它保存在哪里呢?磁盘里!哈哈,BIOS会读取0磁头,0磁道,一扇区的512个字节,这个扇区有叫做MBR(主引导记录),MBR中保存了内核引导程序的开始部分,BIOS将其装入内存执行。512个字节的MBR有些什么呢?这里有必要说说MBR!MBR分区表以80为起始,以55AA为结束,共64个字节。具体的MBR知识自己百度!

MBR:1) 446个字节的引导程序代码

2) 64个字节的分区表,有多少个分区呢。。?这还真不知道!分为4个分区表,一个可启动分区和三个不可启动分区。

3) 2个字节的0XAA55,用于检查MBR是否有效。

需要注意的是,内核引导程序被加载完后,POST部分的代码会被从内存中清理,只留部分在内存中留给目标操作系统使用。

内核引导程序:内核引导程序分两部分:主、次引导程序。主引导程序的主要工作就是收索,寻找活动的分区,将活动的分区引导记录中的次引导程序加载到内存中并且执行。而这个次引导程序就是负责加载内核的并且将控制权交给内核。上面提过内核引导程序有LILO、GRUB、U-Boot、RedBoot。其中前面两个为pc中的,而后面两个是嵌入式的。

内核:内核以压缩的形式存在,不是一个可执行的内核!所以内核阶段首先要做的是自解压内核映像。这里说说编译内核后形成的内核压缩的映像vmlinuz。编译生成vmlinux后,一般会对其进行压缩为vmlinuz,使其成为zImage--小于512KB的小内核,或者成为bzImage--大于512KB的大内核。

vmlinuz结构如图:

Linux 的初始化与启动过程

做了这么多工作终于把linux的内核给引导出来了。!!下面我们来初始化这个千呼万唤始出来的linux内核!

内核初始化:内核会调用一系列的初始化函数去对所有的内核组件进行初始化,由start_kernel()---.....---> rest_init() ----..----> kernel_init() ----....--> init_post() ------到---> 第一个用户init进程结束。

start_kernel():其完成大部分内核初始化的工作。相关的代码去查阅内核的源代码吧!

rest_init():start_kernel() 调用rest_init() 进行后面的初始化工作。

kernel_init():此函数主要完成设备驱动程序的初始化,并且调用 init_post() 启动用户空间的init进程。

init_post():初始化的尾声,第一个用户空间的init 横空出世!其PID始终为1。

init: 内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。找到/sbin/init 后init会根据/etc/inittab (网上的资料都这样说的,我在Ubuntu3.8的内核里找不到,但在Fedora中可以找到!是发行版本不同吧?(求指教)这里附上图片!)文件完成其他一些工作,例如:getty进程接受用户的登录,设置网络等。这里详细说说吧。

fedora19的etc有inittab文件:

Linux 的初始化与启动过程

系统中所有的进程形成树型结构,而这棵树的根就是在内核态形成的,系统自动构造的0号进程,它是所有的进程的祖先。大致是在vmlinux的入口 startup_32(head.S)中为pid号为0的原始进程设置了执行环境,然后原是进程开始执行start_kernel()完成Linux内核的初始化工作。包括初始化页表,初始化中断向量表,初始化系统时间等。继而调用 fork(),创建第内核init进程:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/16269.html