系统的启动过程:任何一台计算机,在开机后,它要做的第一件事情就是引导(Booting),通过引导,计算机为自身搭建好运行环境,为以后OS的启动与运行做好准备。首先,我们来看看一台计算机是如何引导自身的。在机器加电后,电源供电稳定后,电源会传给8284A时钟生成器一个“Power Good”低电位信号,随后8284A会输出有效的RESET信号,使CPU复位,这时CS:IP = FFFF:0000。CPU在这里执行一条jmp far addr类指令,跳转到实际BIOS映射代码的位置,开始执行BIOS代码。
在跳转到BIOS后,首先会先关闭中断,然后开始自检(POST)工作,这个自检主要检测计算机最基本设备的运转状态。其主要包括对,CPU内部寄存器测试,BIOS芯片字节的检查,8237 DMA控制器测试,基本32K RAM检测等最基本内容。由于被检测设备在系统运行中的重要性,因此在此过程中,BIOS一旦检测到任何异常,都将判为致命性错误,系统将被停机。
通过上面的自检后,BIOS开始初始化8259可编程中断控制器,并设置BIOS的8个主要中断向量(int 10h—int 17h),然后初始化并测试CRT视频接口以及显示内存,在确认正常后,执行其内部的显示卡标准驱动程序(注意这里的驱动跟安装操作系统下的驱动是不一样的),这段代码会存放在C0000h,其主要目的是初始化显示卡。然后BIOS会打印显卡信息。
接着BIOS开始检查其他设备,其包括对8259中断控制器测试,8253定时器测试,键盘复位和卡键测试,扩展I/O测试,设置硬件中断向量,扩展RAM测试(这里的RAM测试会检测除0—32K以外的整个RAM空间),.然后BIOS会搜索其他设备的ROM,如果找到,则会执行它们。接着测试ROM-BASIC的字节检查,测试磁盘驱动器(如:FDC等),测试打印机端口和RS-232,并设置他们的地址。
然后打开NMI(不可屏蔽中断),最后就是调用Int 19h进行自举。这一阶段的自检如果发生错误,系统会判断其为一般性错误,并显示出相应的提示信息。在此过程中,BIOS会将检测收集到的数据保存在内存低1K--2K的区域,并将BIOS中断向量表,以及BIOS程序运行所需要的stack保存在内存低0K--1K的地方。
下面就是系统自举工作了,系统调用int 19h进行自举,寻找启动设备,如:软驱,硬盘,光驱等等。找到后系统读取启动设备的0号逻辑扇区(如是软盘就读取0面0道1扇区的整个内容),并将读取的内容放到内存地址为0000:7C00的地方。
当然,如果找不到启动设备,BIOS就会调用Int 18h,并给出相应的提示信息,然后进入ROM-BASIC。至此,BIOS的引导程序结束,CPU开始执行0000:7C00处的代码。在这里需要说明一下的是,BIOS的引导程序是与操作系统无关的,但随后CPU开始执行的代码就开始与操作系统存在较大的相关性了,因此对于不同的操作系统,下面这一部分可能会存在着较大的不同。不过,从目的上来讲,它们是相同的,即都是为将要运行OS的内核(Kernel)作准备。
进入这一部分的首要工作就是执行启动设备的引导程序。硬盘与软盘的对于引导程序的存放结构是不同的。硬盘有一个叫做MBR(Master Boot Record)的扇区,系统会首先执行它,以判断那个分区是启动分区,并读入该分 区的第一个扇区,并执行。并且在这个扇区中还存放着硬盘分区表(DPT),这个表的地位相当重要,因为它包含了
各个分区的诸如:分区类型,起始位置,结束位置等重要参数。下面我们来详细介绍一下MBR的结构。MBR的结构分为三部分,首先是可执行代码,占446个字节,然后是4个分区表,每个占16个字节,共64个字节,最后是签字AA55H
参照赵炯老师《Linux内核完全剖析》中的 一个简单多任务内核实现源码,来分析一下boot.s(源码下载:oldlinux.org/Linux.old/bochs/linux-0.00-050613.zip)
! boot.s
!
! It then loads the system at 0x10000, using BIOS interrupts. Thereafter
! it disables all interrupts, changes to protected mode, and calls the
BOOTSEG = 0x07c0
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
SYSLEN = 17 ! sectors occupied.
entry start
start:
jmpi go,#BOOTSEG //跳转到段BOOTSEG,段内偏移为go的地方执行代码,此时为实模式,所以BOOTSEG为段地址,jmpi后面跟的是偏移和段选择符。为什么要跳转到这里,上面说过系统调用int 19h进行自举,寻找启动设备,如:软驱,硬盘,光驱等等。找到后系统读取启动设备的0号逻辑扇区(如是软盘就读取0面0道1扇区的整个内容),并将读取的内容放到内存地址为0000:7C00的地方。
通过调试也可以查看到内容被读取到内存地址为0000:7C00的地方