计算机那些事(1)——开机启动过程
首先用一张图来大致了解一下计算机启动的整个过程。

上电
按下主机的电源键后,计算机开始启动,主板上电后开始初始化其固件(firmware)。固件是一些固化在芯片组上的程序,它会试图去启动CPU。如果启动失败(例如CPU坏了或没插好),计算机就会死机并给出错误提示(如某些版本的主板固件会发出蜂鸣警告)。这种状态称为“zoombie-with-fans”。
如果前一个阶段未出错,就开始加电工作,在多CPU或多核CPU情况下,某一个CPU会被随机选取作为启动CPU(bootstrap processor,BSP)运行BIOS内部的程序。其余的CPU(application processor,AP)保持停机直到操作系统内核显式地使用它们。
2000年以前的计算机主板上均使用BIOS,如今绝大多数计算机采用的是EFI(Mac用的就是EFI)或UEFI,BIOS正在逐步被淘汰。基于EFI、UEFI的开机过程与传统的BIOS不尽相同,本文将以传统的BIOS,Intel CPU为例介绍开机过程。
此时CPU工作模式为实模式,该模式下地址总线是20位,寻址范围是0x00000~0xFFFFF的1M范围。这也就解释了为什么BIOS的容量只有1MB。
Intel CPU用三种运行模式: 实模式、32位保护模式、64位保护模式。实模式: Intel 8086的寻址方案,为了商业连续性,兼容了这古老的方案;保护模式: 采用了虚实地址转换方案。
BIOS启动之初,内存是空的。此时CPU处于实模式,内存的地址映射均为硬连接的设备。内存映射图如下图所示:

重置向量
CPU启动后其大多数寄存器会被初始化为预定的值,包括指令寄存器(instruction pointer, EIP),它保存着CPU将要执行指令的内存地址。此时CPU会有一个特殊行为,其会对EIP的初始值加上一个基址寄存器的值,生成一个32位的地址0xFFFFFFF0。之所以称为特殊行为,是因为实模式下CPU只能寻址1MB地址空间,而这个32位地址已经大于1MB的内存限制。因此,0xFFFFFFF0也被称为重置向量(reset vector),参考上图0xFFFFFFF0处的标识。
于是,CPU开始执行0xFFFFFFF0地址处的指令,该地址处是一条JUMP指令,这条指令清空了基址寄存器的值,并让指令跳回到BIOS开始处(物理地址为0xF0000,参考上图0xF0000处的标识)以执行BIOS。
BIOS内部可以分成两个区块: code block(普通程序)、boot block(引导程序)。上电后,boot block会先被执行,它会检查code block的代码是否正确,如果正确,就会转到code block继续执行下去。
BIOS初始化
0xF0000地址实际上是BIOS中的boot block的开始处。在这个阶段,会初始化部分硬件。系统的CPU、USB只有部分被初始化。
BIOS POST(加电自检)
初始化完成后,CPU跳转到0xA0000地址处(参考上图640KB处)进行BIOS加电自检(power on self test, POST)。这个过程会检查计算机的各项组件,如CPU、显卡、内存、鼠标、键盘等。如果找不到内存或者键盘都有可能让BIOS停止工作并且打印一些相关的错误信息,如果找不到显卡BIOS会发出蜂鸣警告(因为无法显示画面)
当CPU执行到0xC0000地址处(参考上图768KB处),开始寻找其他设备的ROM,如果找到任何其他设备的BIOS,它们也会被执行。
下一步,显卡就会显示BIOS界面,并进行更深入的检查。
BIOS记录系统设定值
检查完成后,BIOS会根据自己的“系统资源表”,对系统进行进一步确认,从而确定计算机配有哪些资源或设备。例如BIOS支持随插即用,它会检测并配置随插即用设备。
然后BIOS会遵循高级配置电源接口(Advanced Configuration Power Interface,ACPI)在内存中设置好一系列的数据来描述硬件信息,以便被操作系统内核利用。
搜索MBR
到这一步,BIOS开始尝试加载操作系统。它会从硬盘,光驱,软驱,网络等几个地方依次寻找操作系统(用户可以在BIOS设定中修改查找的优先级)。如果找不到操作系统,BIOS会停机并给出错误信息。
假设在硬盘上找到了操作系统,它会首先读取硬盘上的大小为512B的0号扇区,这个扇区被称为主引导记录(master boot record,MBR),其包含三部分:
(1) 启动代码
(2) 硬盘分区表
(3) 结束标志字
BIOS读完磁盘上的MBR之后会把它拷贝到内存0x7C00地址处,然后CPU跳转到该内存地址执行MBR里的指令。下图可以帮助大家理解MBR的结构。

启动代码中的硬盘引导程序的主要作用是检查分区表是否正确并且在系统硬件完成自检以后将控制权交给硬盘上的引导程序(Windows里面是Windows MBR Loader,Linux的话可能是LILO或者GRUB)。
磁盘分区表是固定的,一个64字节的区域,每16字节为一个条目,描述硬盘的分区信息(这样就可以在同一磁盘的不同分区上安装不同的操作系统)。所以采用了MBR格式分区表的文件系统最多能安装四个操作系统。
引导操作系统
执行MBR内部启动代码中的引导程序可以引导操作系统。Linux的引导程序LILO和GRUB可以处理各种不同的操作系统,文件系统和引导配置。
- 根据MBR启动代码中的引导程序加载包含了额外引导代码的其他扇区。这些扇区可能是一个分区的引导扇区,也可能是MBR安装时硬编码到MBR代码中的某个扇区。
- 执行步骤1载入的引导代码读取配置文件(例如,GRUB中是grub.conf,windows中是boot.ini)。然后它会显示启动选项,或直接启动操作系统
- 此时引导程序开始引导操作系统内核。它必须能够识别内核所在引导分区的文件系统。在Linux中,这一步是读取一个类似“vmlinuz”的文件,加载至内存中并跳转到内核启动代码。
此时我们回头再看第一张图,从引导程序到早期的内核初始化过程,此时内核解压开始了。
参考
[1] How Computers Boot Up
[2] 即将换掉传统BIOS的UEFI
(完)