一 预备
在8086系统CPU中,有两个寄存器CS和IP用于保存CPU接下来需要执行的指令地址,具体计算方法为 CS<<4+IP
例如CS=0x7c0,而IP=0x0001,则CS<<4+IP=0x7c01
,因此接下来CPU会从内存地址0x7c01中读取一条指令执行。
当取出一条指令执行后,IP的内容会被自动加当前指令占用的字节数,从而指向了下一条指令,于是,处理器又从取出下一条指令执行。
二 BIOS
在计算机刚加电时,CS和IP会被初始化成固定的默认值,CS=0x7c0,IP=0x0000,因此计算机启动时会从0x7c00这个位置开始执行。
0x7c00这个位置被固化了BIOS系统,不要纠结这个系统从哪来的,因为它本身就是在那的。
因此,相当于计算机刚启动时,就会立即开始执行BIOS系统。
BIOS是指基本输入输出系统(Basic Input/Output System),它只做了一件事:从磁盘中把安装在磁盘中的操作系统(如我们使用的windows,linux)加载到内存中,然后通过修改CS和IP寄存器的内容,把CPU的执行控制权交到操作系统中。
具体一点,BIOS会把磁盘中安装的操作系统加载到内存中的0x7c00处,,然后修改CS=0x7c0, IP=0x0000,当CPU读取下一条指令执行时就会读到具体操作系统的指令。
二 主引导扇区
通过上面的描述,你可能觉得BIOS会负责把操作系统的所有内容都加载到内存中去,这但是实际上并不是这样的。
BIOS并不知道具体需要怎样加载操作系统(这很正常,不同的操作系统有不同的加载方式),于是BIOS规定:任何安装在磁盘中的操作系统,必需在磁盘的第0扇区存放一段引导代码,BIOS只把这一段引导代码加载到内存中执行,而具体应该如何加载整个操作系统,由操作系统的编写人员自行在引导代码中定义。
也就是说,BIOS只加载并执行引导代码,而引导代码自行加载整个操作系统。另外值得一提的是,这块存放引导代码的第0扇区被称为主引导扇区,操作系统就是从这里开始的。
这里涉及到磁盘的内容:磁盘中的内容是分扇区维护的,每个扇区共512个字节,第0个扇区为0-511字节,第1个扇区为512-1023字节,依此类推。
将程序交给主引导扇区代码执行后,BIOS就正式功成身退了,毕竟接下来执行的引导代码已经属于操作系统的内容,而具体是怎么执行的,不同的操作系统当然有不同的方法。不过即然已经到操作系统执行了,那么我们可以认为计算机已经启动完毕了。
以上就是计算机启动的整个过程,简单总结就是:
开机时默认初始化CS=0x7c0, IP=0x0000,CPU自动来始执行0x7c00的BIOS代码
BIOS代码把磁盘中第0号扇区的内容加载到0x7c00处,然后修改CS=0x7c0, IP=0x00000,CPU又开始执行操作系统的引导代码
三 linux0.11 的主引导扇区代码
当CPU开始执行引导代码后,计算机就算启动完成了,但是操作系统还没有完全启动成功,接下来以linux0.11为例说说引导代码后续的内容,linux0.11已经是非常老的内容了,但是对于学习操作系统来说,研究它就够了。
linux0.11的主引导扇区代码定义在文件bootsect.s中,这是一段汇编代码。会把操作系统剩下的所有内容都加载到内存中。
操作系统所有的代码都加载到内存中去之后,并不能真正的交给操作系统处理,因为操作系统还需要初始化,所以在此之前还需要执行一断setup.s汇编代码,用于初始化系统。
因加载完毕后bootsect.s会通过修改CS:IP值的方式,让CPU去执行setup.s,至此,bootsect.s代码就算是完成使命了。
setup.s代码存放在磁盘的0-4扇区,会被加载到内存的0x90200处。它主要是初始化了一些系统的基本内容,如内存长度,显卡类型等。然后会把操作系统剩下的核心内容移动到0x0000地址处,再通过修改CS:IP的方式,让CPU开始执行操作系统的核心内容(以下称操作系统内核)。
操作系统内核开始执行前还会先进一步初始化系统,在内核最开始的地方是一个head.s代码段,这里主要是使用一些牛逼的手段调用到C语言的main函数。从这里开始,操作系统终于进入到了C语言编写的内容。
在main函数的头部,还是会先进行进一步的初始化,这个初始化更加仔细,而且也更多,例如初始化了内存管理用的页表,初始化了中断向量表等。到此,操作系统正式启动完成。
总结:linux0.11启动过程:
- 先执行主引导扇区代码,即bootsect.s代码,把操作系统的所有内存加载到内存中
- 再执行setup.s代码,它会初始化一些硬件数据,并且把操作系统的内核移动到0x0000地址处
- 再跳到0x0000地址处开始执行,执行到的是head.s代码段,这里会通过一些比较牛逼的方式调用到C语言编写的main函数中
- 在main函数中会进一初始化系统,如中断向量,内存管理的数据结构等
再跟上面的结合起来,整个流程就是:
开机 =》执行BIOS系统 =》执行主引导扇区代码(bootsect.s)=》执行setup.s =》执行head.s =》执行C语言main函数。