操作系统的启动

分类: 操作系统

操作系统的启动

回顾: 刚开始上电,操作系统在硬盘上。而计算机的工作是取指执行,也就是要把代码放到内存上。所以我们第一步就是要把操作系统从磁盘上载入到内存,也就是二中比喻的:把菜谱交给厨师。这个过程在上一节bootsect.s进行。它将操作系统setup分段读取,并打印logo,然后把操作系统后面的system部分读入,最后将bootsect自己从前几个内存位置移走,通过指针交接给setup执行。

setup

setup 模块就是内核在正式接管电脑前进行的“人口普查”和“资产登记”

因为当 CPU 进入 32 位保护模式(Protected Mode)后,原本由 BIOS 提供的便捷中断服务(如读取磁盘、获取内存容量)就再也无法直接使用了。内核必须在“关灯”切换模式之前,把所有关键的硬件规格记录在案。 | 为什么会有进入32位保护模式这个动作? 因为在刚开机的时候,CPU 运行在 实模式(Real Mode)。这时候整个世界是 16 位的,地址空间 1MB,很多硬件初始化工作都是通过 BIOS 提供的中断来完成的。而处理器设计为了兼容性永远从实模式启动,但是实模式的16位支撑不了现代操作系统。所以需要先从16位引导跳到32位才能接着启动系统。

第一阶段:硬件参数搜集

在进入保护模式之前,内核需要利用 BIOS 中断(这些中断在保护模式下将不可用)来获取机器的物理配置:

光标位置:用于后续屏幕输出。

内存大小:确定系统可用的物理内存边界。

显卡参数:确定显示模式(文本或图形)。

硬盘参数(设备根号):获取硬盘的柱面数、磁头数等,存放在特定的内存位置(如 0x90000 处)供内核后续使用。

第二阶段:移动内核镜像

为了给后续的内存布局腾出空间,setup 会将紧随其后的 system 模块(内核主体)从内存的高地址处搬移到起始地址 0x00000

第三阶段:准备描述符表 (GDT & IDT)

位将要转向的保护模式的内存管理机制的基础做准备。

加载 GDT (Global Descriptor Table):规定CPU 怎么理解一个地址、有没有权限访问。定义代码段和数据段的基址与权限。此时通常会将基址设为 0,实现平坦内存模型。 用处:为进入保护模式的 CPU 提供一个“新的地址解释规则

加载 GDT 是在告诉 CPU:

> 从现在开始,地址不是简单拼接了,而是需要经过“规则校验”。

这就是“保护”模式名字的来源。
操作系统的内存管理:
第一层:分段(GDT)
    解决“地址是否合法、权限对不对”

第二层:分页(Page Table)
    解决“虚拟地址映射到物理地址”

第三层:内核策略
    解决“哪个进程能用多少内存”

加载 IDT (Interrupt Descriptor Table):当发生“事件”时,CPU 应该跳到哪里执行代码? 虽然此时还没有真正的中断处理函数,但必须先初始化一个空的或占位的表。

第一类:异常(Exception)
比如:

除 0

访问非法内存(页错误)

执行非法指令

第二类:硬件中断(Interrupt)
比如:

键盘按下

网卡收到数据

定时器触发

第三类:软件中断
比如:

int 0x80(早期 Linux 的系统调用方式)

在实模式里,有一个叫 IVT(中断向量表)的东西,但是转向保护模式就失效了。所以现在重新构建保护模式下的这套机制

第四阶段:开启保护模式

这是“惊险的一跳”。通过设置控制寄存器 CR0 的 PE(Protection Enable)位,CPU 正式进入 32 位保护模式。

操作系统setup

内存地址 阶段一 (Real Mode) 阶段二 (Protected Mode)
0x00000 BIOS 中断向量表 内核 system 模块 (head.s)
0x90000 bootsect 副本 硬件参数存储区
0x90200 setup 代码 (完成使命后被废弃)

从此操作系统接管硬件。