计算机加电后发生了什么?代码 & 理论分析
本文最后更新于:2022年9月22日 晚上
计算机刚被加电的时候,RAM中空空如也,OS存储在磁盘中,CPU只能执行被加载到内存中的代码,那么操作系统的代码是如何被CPU执行的呢?
1.BIOS阶段
首先,CPU被设计为加电瞬间CS:IP指向0xF000:0xFFF0,也即是0xFFFF0的位置。此时是在实模式下,这个地址即是实际物理地址,ROM BIOS被设计为存储在这个位置,它的代码入口地址即是0xFFFF0。BIOS的代码地址范围:0xFE000~0xFFFFF,这些代码的作用是:
- 进行硬件自检,检测CPU、显卡、内存、硬盘的信息
- 在内存最开始的区域构建中断向量表(0x00000~0x003FF,1KB内存空间)
- 构建BIOS数据区(0x00400~0x004FF)
- 在上述数据区后面约57KB的位置加载8KB左右的、与中断向量表对应的中断处理程序
2.引导扇区(bootsect.s)
bootsect也被称为MBR(master boot record)
CPU收到int 0x19中断,在中断向量表中找到对应的中断服务程序的入口地址,并执行该中断服务程序对应的功能:将第一扇区(512个字节)的程序,也即是bootsect加载到内存中0x07C00地址处并跳转到此处
接下来,bootsect本身512个字节会被从0x7C00复制到内存的0x90000处
1 |
|
之后,bootsect触发0x13中断将setup.s程序加载到内存的0x90200处
1 |
|
再往后,再次触发0x13中断将系统模块装载入内存0x10000地址处
1 |
|
由于这部分代码比较长,因此时间也比较久,屏幕上会打印出“loading system”进行提醒
1 |
|
最后,确认根文件系统的设备号,并跳转到setup函数的0x90200地址处
1 |
|
3.setup
利用BIOS中断读取机器数据,将其保存到0x90000地址处(会覆盖掉bootsect代码),这些数据包括硬件参数表、光标位置、根设备号等等
1 |
|
将位于0x10000地址处的内核程序复制到0x00000地址处(会覆盖掉终端向量表和BIOS数据区)
1 |
|
为了给后面的保护模式做准备,setup程序还要对中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR)进行初始化设置
之后,设置 CPU 的控制寄存器 CR0(也称机器状态字),从而进入 32 位保护模式运行,并跳
转到位于 system 模块最前面部分的 head.s 程序继续运行
- 打开A20,实现4GB寻址
- 将CR0寄存器的第0位(PE)置零,将处理器工作方式设置为保护模式
4.head程序
之前提到system模块会被复制到0x00000开始的位置,head程序其实是system模块的一部分,而且是最开始的部分,0x00000其实也是head程序的起始地址。head.s 与前面bootsect.s和setup.s汇编不同,使用的是AT&T格式的汇编
- 加载各个数据段寄存器,重新设置中断描述符表 idt,共 256 项,并使各个表项均指向一个只报错误的哑中断程序
- 重新设置全局描述符表 gdt
- 使用物理地址 0 与 1M 开始处的内容相比较的方法,检测 A20 地址线是否已真的开启
- 设置管理内存的分页处理机制,将页目录表放在绝对物理地址 0 开始处紧随后面放置共可寻址 16MB 内存的 4 个页表,并分别设置它们的表项
- 最后利用返回指令将预先放置在堆栈中的/init/main.c 程序的入口地址弹出,去运行 main()程序
实模式与保护模式
实模式:
- 寻址
- 段寄存器存放段的基地址,和偏移值共同确定内存地址,此内存地址即是真实物理地址,最大寻址空间是20位,1MB
- 中断机制
- 使用中断向量表,其实位置在0x00000,位置固定
保护模式:
- 寻址
- 段寄存器存放段描述符表中某一项的索引,索引值所指向的段描述符项包含以下信息:
- 要寻址的内存段的基地址
- 段的最大长度值
- 段的访问级别等信息
- 最大寻址空间是32位,4GB
- 段寄存器存放段描述符表中某一项的索引,索引值所指向的段描述符项包含以下信息:
- 中断机制
- 使用中断描述符表(IDT),可以由操作系统灵活安排,通过IDTR寄存器定位位置