相信很多人和我一样,从接触计算机开始,就一直在思考这个问题:当按下电脑的开机键之后,到底发生了什么?为什么这样就能启动电脑?
这篇文章我试着分析一下计算机的启动原理和过程。

CPU
CPU 全称是中央处理器,是计算机的核心部件之一,包括控制单元、运算单元、寄存器以及时钟等部分,它的主要功能是执行计算机指令以及处理各种数据,包括进行基本的算术运算,以及从存储器或者各种接口设备中读取、写入数据。
CPU 内部包含若干个寄存器,用来暂存数据或者控制 CPU 的运行。以早期的 8086 为例,常见的寄存器有 AX、BX、CX、DX、CS、DS、SS、ES、DI、SI、SP、BP、IP、FLAGS 等。
我们都知道,计算机最终执行的是二进制形式的机器指令。例如某条机器指令可以表示将一个值加载到寄存器 AX 中。由于直接阅读二进制指令非常困难,因此人们发明了汇编语言。
例如,用 Intel 语法表示:
mov ax, 0
存储器
存储器就是我们通常所说的内存。CPU 是计算机的核心,但没有存储器也是无法工作的,因为 CPU 需要执行的指令和处理的数据都需要有地方存放。
CPU 通过地址总线、数据总线和控制总线与存储器进行连接,从而可以从指定的内存地址读取或者写入数据。
上课的时候,老师经常会告诉我们,内存属于易失性存储器(RAM),必须保持供电才能保存其中的数据,断电之后数据就会丢失。
于是这里就产生了一个问题:
既然内存断电之后没有数据,而 CPU 又不能凭空产生指令,那么计算机刚开机的时候,CPU 到底从哪里获得第一条指令?
答案就是非易失性存储器。
非易失性存储器
说到非易失性存储器,我们可能首先想到 U 盘、硬盘或者光盘等设备。它们都可以在断电之后保留数据。
除此之外,还有一类非常重要的非易失性存储器——ROM。
ROM 断电之后仍然可以保存其中的数据,因此早期计算机通常利用 ROM 保存一些必须长期存在的程序。
计算机中的很多设备都包含 ROM,例如显卡,而在传统 PC 中,主板上的 ROM 通常用于保存 BIOS(基本输入输出系统)。
ROM 同样属于存储器的一种,CPU 可以直接读取其中的数据。不同的是,传统意义上的 ROM 不能像普通 RAM 一样随意写入数据。
计算机的启动
说到这里,问题似乎就容易理解了:
只要事先把一段启动程序写入 ROM,计算机通电以后,让 CPU 从 ROM 中执行这段程序,不就可以启动了吗?
实际上,早期 x86 PC 的启动过程正是建立在这样的机制之上。
以传统的 BIOS 和 x86 实模式启动过程为例,大致可以分成下面几个阶段:
- 按下电源键之后,主板完成基本的供电和初始化。CPU 复位后,会从预先规定的地址开始取指令。
- 在 8086 的实模式下,CPU 复位后
CS=0xFFFF、IP=0x0000,因此形成的物理地址为0xFFFF0。这个地址对应主板上的 ROM 区域。 0xFFFF0处通常是一条跳转指令,用来跳转到 BIOS 中真正的初始化代码。- BIOS 开始执行加电自检(POST),检查 CPU、内存、键盘、显卡等基本硬件是否能够正常工作。
- 完成基本硬件检查后,BIOS 按照启动顺序寻找可启动设备。
- 找到启动设备后,BIOS 将设备的第一个扇区读取到内存
0x7C00,然后把执行权交给这段代码。
这里需要注意,POST 完成并不意味着操作系统已经启动。这只是 BIOS 完成了硬件初始化和基本检查,接下来还需要加载操作系统的启动程序。
MBR
在传统 BIOS 启动方式中,BIOS 会按照设置好的启动顺序检查硬盘、U 盘或者光盘等设备。
以传统硬盘 MBR 启动方式为例,BIOS 会读取启动设备的第一个扇区,也就是前 512 字节的内容,并检查其最后两个字节是否为:
0x55 0xAA
如果存在这个签名,BIOS 就认为这是一个有效的启动扇区,并将其加载到内存 0x7C00,然后从这里开始执行。
启动设备的第一个扇区就是我们常说的 MBR(Master Boot Record,主引导记录)。传统 MBR 中除了启动代码之外,还包含分区表以及最后的 0x55AA 启动签名。
MBR 的主要任务之一就是继续加载更完整的引导程序,然后由引导程序进一步加载操作系统。
所以整个启动过程可以简单理解为:
CPU复位
↓
执行ROM中的BIOS代码
↓
POST硬件自检
↓
寻找启动设备
↓
读取启动扇区到0x7C00
↓
执行MBR/启动代码
↓
加载操作系统引导程序
↓
加载操作系统
更详细的启动流程,请看下图:

综上,计算机启动过程中有几个关键点:
- ROM 中预先保存了最初的启动程序;
- CPU 复位之后,会从一个预先规定的位置开始执行指令;
- BIOS 完成最基本的硬件初始化之后,会寻找可启动设备;
- 启动设备上的 MBR 或启动扇区负责继续加载后面的启动程序;
- 最终由引导程序加载操作系统。
对于我们来说,CPU 和最初的固件已经由硬件系统确定,真正有意思的部分则是启动设备上的第一段代码到底是如何工作的。
下面我们自己完成一个简单的 MBR,不依赖操作系统,在计算机启动时直接显示:
Welcome to https://www.hitoy.org/
DISADDR EQU 0xb800
DISCOLOR EQU 0x02
LOADADDR EQU 0x7c00
section .code vstart=LOADADDR
_start:
; 注意:实模式下不能直接设置 CS
; mov ax,LOADADDR
; mov cs,ax
; data addr
mov ax,string
mov si,ax
; display segment
mov ax,DISADDR
mov ds,ax
mov bx,0
show:
; Copy Char to CL
mov cl,[cs:si]
mov ch,0
cmp cx,0
je end
mov ch,DISCOLOR
mov [ds:bx],cx
inc si
add bx,2
jmp show
end:
jmp $
string:
db "Welcome to https://www.hitoy.org/",0
times 510-($-$$) db 0
db 0x55,0xaa
这里最后的:
times 510-($-$$) db 0
db 0x55,0xaa
就是把整个启动代码填充到 512 字节,并在最后两个字节写入传统 MBR 的启动签名。
使用 NASM 编译成 bin 格式文件,再将其写入启动设备的第一个扇区,启动计算机之后,就可以直接看到我们自己编写的文字。

从这个实验可以看到,计算机启动并没有什么神秘之处。最开始只是 CPU 从一个固定的位置开始执行指令,然后一级一级地把控制权交给后面的程序,最终由引导程序把操作系统加载起来。
评论1
欢迎分享你的看法,也欢迎补充不同的实践经验。