我们在使用计算机时,当我们按下电源按钮,我们会发现计算机先进入BIOS,然后再进入操作系统,最后计算机成功启动。那么,计算机的启动过程是怎么样的呢?为什么要进入BIOS?另外,当计算机存在双系统时,我们还可以选择进入到哪个系统,这个又是怎么实现呢?想要比较详细地理解整个过程,我们先来看看计算机的硬件构成和主板芯片组的系统结构框图。
1 计算机芯片组和主板结构
要想深入了解计算机的启动过程,我们先来看看目前计算机主板的系统结构:
1.1 系统总线
1.2 北桥芯片
在主板芯片组中北桥芯片通过前端总线连接CPU,通过内存总线连接内存,通过图像总线连接显卡,通过内总线连接南桥芯片。北桥芯片是CPU与内存、显卡和其他外设进行通信的桥梁。
CPU通过前端总线与北桥芯片连接,再通过系统总线实现CPU对其他部件的访问。前端总线的频率,以MHZ为单位,它是一个很重要的频率,因为它和时钟倍频器决定着CPU的工作频率,同时前端总线的频率为系统总线频率提供时钟源,此外,前端总线频率也决定着CPU与外设的访问速度,进而决定该计算机系统的运行效率,所以并不是搭载主频越高的CPU的计算机,它的运行效率越高。
1.3 南桥芯片
2 计算机系统内存地址分配
CPU要想和不同的设备通讯,就必须知道该设备的地址范围,并通过地址总线选择具体的设备和具体的地址,再通过控制总线来控制数据的流向,数据总线来进行规定的数据传输,这就需要为每个设备分配一个CPU可以寻址的内存地址范围,事实上,内存映射表是主板提供的。对于32位X86架构的CPU来说,它的地址总线是32位的,因此最大寻址的空间是2^32=4G,在该体系下一个典型的内存映射如下图:
0~4GB的地址范围属于物理地址,而对于CPU的保护模式来说,它使用的是逻辑地址,逻辑地址需要地址转换器转换为物理地址,进而实现对设备的访问。CPU的实模式就直接使用物理地址的,而且目前的CPU在上电后默认的模式都是实模式,这个是为了满足向下兼容的特性而设计的,因为80386以前的CPU只有实模式,而相应设计的操作系统就是工作在实模式的,因此为了使后续的CPU能够运行早期的操作系统,因此在设计上使CPU在启动时默认采用实模式。事实上,在80386即以后出现的CPU才支持保护模式。
上图中,内存大概占了差不多3G的空间,大概1G的空间分配到支持PCI、APCI等总线的外设了。注意在实模式下,只有1M的地址空间分配到RAM。
3 启动过程
”建立用于描述各个设备的数据表,这些数据表在后面会被OS的内核用到。
可以看到,一个磁盘可分为多个扇形区域,也可以分为多个磁轨track,而扇形区域和磁轨重叠的部分就是扇区sector,每个sector的容量是固定512byte的。另外,一个硬盘会有多个磁盘面,每个盘面的同一个磁轨组成一个磁柱cylinder,每个磁盘面有一个读写头header,就是说一个磁盘有两个磁头(2个次盘面)。虽然内外磁轨的长度不一样,但是每条磁轨上的扇区数还是一样的,就是说内圈密度大,外圈密度小。
标准MBR结构
地址 | 描述 | 长度 | |||
Hex | Oct | Dec | |||
| | 0 | 代码区 | 440 (最大446) | |
| | 440 | disk signature (optional) | 4 | |
| | 444 | 一般为空值; 0x0000 | 2 | |
| | 446 | Table of primary partitions (四个16 byte的主分区表入口) | 64 | |
| | 510 | 55h | MBR 有效标志: 0x55AA | 2 |
| | 511 | AAh | ||
MBR, 总大小: 446 + 64 + 2 = | 512 |
单个分区表结构
偏移 | 长度(字节) | 描述 |
00H | 1 | 分区状态:0x00-->非活动(不可引导)分区; 0x80--> 活动(可引导)分区; 其它数值没有意义 |
01H | 1 | 分区起始磁头号(HEAD),用到全部8位 |
02H | 2 | 分区起始扇区号(SECTOR),占据02H的位0-5; 该分区的起始磁柱号(CYLINDER),占据 02H的位6-7和03H的全部8位 |
04H | 1 | 分区类型 |
05H | 1 | 分区结束磁头号(HEAD),用到全部8位 |
06H | 2 | 分区结束扇区号(SECTOR),占据06H的位0-5; 该分区的起始磁柱号(CYLINDER),占据 06H的位6-7和07H的全部8位 |
08H | 4 | 分区起始相对扇区号 |
0CH | 4 | 分区总的扇区数 |
BIOS会将MBR中的所有数据拷贝到物理地址0x7C00,然后再跳到该地址执行代码。此时的代码对于windows来说可以是MBR Loader,对于Linux来说可以是LILO或GRUB。
- Linux在MBR中存放第一阶段的引导代码,称为阶段1;
- 因为阶段1的代码很小,只占MBR中的一小部分,因此阶段1的代码将存放在其它扇区的引导代码下载到MBR中,这些扇区一般为其他分区的boot sector,也可以被Linux在安装时将某个扇区直接写到MBR中,这样阶段1的代码可以直接根据该信息直接跳到目标扇区去下载代码,这里统称为boot sector;
- 当boot sector的代码都被下载到MBR后,该代码将执行,此时称为阶段2,引导代码将读取一个配置,该配置文件中存储有OS的相关信息,如果你安装了多系统,此时会让你选择启动哪个OS;
- 当引导代码知道要启动哪个操作系统后,引导代码会将boot sector所在分区中的内核文件拷贝到内存中,然后再跳到内存中执行内核启动代码,自此操作系统开始启动。
3.1 Linux LILO启动过程
- 此时LILO第一阶段的代码会根据相关信息,到指定的扇区sector将阶段二的代码拷贝到0x00096c00,并且在0x000969ff~0x00098000范围建立实模式堆栈,并跳转到0x00096c00执行阶段二的代码。阶段二的代码将根据分区表寻找可以启动OS的分区,并通过BIOS的底层驱动打印信息提示用户选择启动哪个操作系统。若启动其他的OS,LILO将相应分区的boot sector中的启动代码拷贝到内存中并执行。
- 若启动的是Linux,则将内核镜像文件的前512字节存放到内存地址0x00090000,而镜像文件中的setup()函数存放到地址0x00090200。
- LILO再将镜像文件的剩下部分拷贝到相应的其他地址(如果使用指令"make zImage"则存放在地址0x00010000;如果使用指令"make bzImage"则存放在地址0x00100000)。
- 跳转执行setup()代码,该代码将执行以下工作:(1)调用BIOS底层接口建立一个描述系统物理内存布局的表;(2)设置键盘持续按下时判定“重复敲击”事件的阈值时间;(3)初始化显卡;(4)初始化硬盘控制器;(5)检查IBM Micro Channel Bus(MBA);(6)检查PS/2接口的设备;(7)检查BIOS是否支持Advanced Power Management(APM)功能;(6)