OS 启动的第一步是 BIOS ,BIOS(Basic Input/Output System,基本输入输出系统)是计算机系统中的一个非常关键的组件,其主要职责可以概括为系统的启动和基础硬件管理。

BIOS 的工作流程和功能

BIOS 的工作流程和功能可以分为以下几个主要方面:

  1. 开机自检:电脑开机时,BIOS 首先检查电脑的基本硬件是否正常工作,比如内存、键盘等。

  2. 准备硬件:BIOS 设置并准备好电脑的主要硬件,比如 CPU 和硬盘,为启动操作系统做好准备。

  3. 引导过程:BIOS 确定从哪个设备(硬盘、U 盘等)启动电脑,并开始加载操作系统。

  4. 系统设置:BIOS 提供一个界面,让用户可以设置或更改电脑的硬件配置。

总的来说,BIOS 就像是电脑启动时的指挥官,负责检查硬件、准备系统,并引导操作系统的启动。

第一条指令

IBM PC 的启动过程是一个经典的计算机启动流程,我们可以通过它来理解计算机如何从零开始加载操作系统。

首先,我们要了解的是 IBM PC 的内存布局。在 IBM PC 中,当电脑开机时,CPU 开始从一个固定的物理地址执行代码,这个地址就是0x000ffff0。这个地址位于 ROM BIOS 的最顶端,占据了 64KB 的内存空间。ROM BIOS 是只读存储器中的基本输入输出系统,负责在计算机启动时进行硬件检测、初始化,并加载操作系统。

+------------------+  <- 0x00100000 (1MB)
|     BIOS ROM     |
+------------------+  <- 0x000F0000 (960KB)
|  16-bit devices, |
|  expansion ROMs  |
+------------------+  <- 0x000C0000 (768KB)
|   VGA Display    |
+------------------+  <- 0x000A0000 (640KB)
|                  |
|    Low Memory    |
|                  |
+------------------+  <- 0x00000000

接下来解释地址 0x000ffff0CS = 0xf000IP = 0xfff0组成。在这里,CS(Code Segment,代码段寄存器)和 IP(Instruction Pointer,指令指针寄存器)是 x86 架构中用于指定指令地址的寄存器。在 IBM PC 开机时,CS 的值被设置为0xf000,而 IP 的值被设置为0xfff0。这意味着 CPU 从物理地址0x000ffff0(等于0xf000 * 16 + 0xfff0)开始执行指令。

最后,我们来看看第一条指令是什么。在0x000ffff0这个地址上,存放的是一个跳转(jmp)指令。这条指令告诉 CPU 跳转到一个新的地址,开始执行那里的代码。在这个例子中,跳转的目标地址是 CS = 0xf000,IP = 0xe05b。这意味着跳转后的物理地址是0xfe05b(等于0xf000 * 16 + 0xe05b)。跳转到这个地址后,CPU 开始执行 BIOS 中的代码,进行进一步的初始化和启动过程。

结合以上细节,我们可以理解 IBM PC 是如何从一个固定的物理地址开始,通过设置特定的寄存器和执行跳转指令,进而开始整个的启动过程的。

设置中断描述符表(Interrupt Descriptor Table, IDT)

IDT 是一个数据结构,它告诉 CPU 当特定的中断发生时应该执行哪些代码。例如,假设你的电脑有一个键盘。当你按下键盘上的一个键时,键盘会向 CPU 发送一个信号,这个信号被称为“中断”。BIOS 在 IDT 中为这种类型的中断配置了一个特定的入口(或“描述符”)。这个入口定义了当按键中断发生时应该执行的函数的地址。

当中断发生时(比如你按下了键盘上的一个键),CPU 会暂停当前正在执行的任务,并根据 IDT 中的信息跳转到相应的中断处理函数。这个中断处理函数将执行必要的操作来响应中断。在按键的例子中,中断处理程序可能会读取按下的键的信息,并将其传递给操作系统。一旦中断处理函数完成其任务,它会通知 CPU 中断处理已经完成,CPU 随后会恢复之前被中断的任务。

中断描述符表(IDT)是一个关键的计算机结构,它用于管理中断处理程序的地址。为了更直观地理解 IDT,我们可以将其想象成一个表格,每一行代表一个中断向量,其中包含了处理该中断所需的信息。下面是一个简化的图形化表示:

+------------------+------------------+------------------+------------------+
| 中断向量号(Index) | 偏移地址(Offset) | 段选择器(Selector) | 属性(Attributes)  |
+------------------+------------------+------------------+------------------+
|        0        |  0x0000FFFF      |     0x08        |   0x8E          |
|        1        |  0x0000FFEF      |     0x08        |   0x8E          |
|        2        |  0x0000FFDF      |     0x08        |   0x8E          |
|       ...       |      ...         |     ...         |    ...          |
|        N        |  0x0000FXXX      |     0x08        |   0x8E          |
+------------------+------------------+------------------+------------------+
  • 中断向量号(Index):这是中断的唯一标识符。例如,键盘中断、时钟中断等都有唯一的向量号。

  • 偏移地址(Offset):这是处理该中断的函数在内存中的地址。当中断发生时,CPU 会跳转到这个地址执行相应的处理程序。

  • 段选择器(Selector):这是一个指向处理程序所在段的指针。在大多数现代操作系统中,这通常指向一个代码段。

  • 属性(Attributes):这定义了中断门的类型和权限,例如,它可能指明这是一个 32 位的中断门,以及是否允许用户模式下的程序触发这个中断。

请注意,这只是一个简化的表示。实际的 IDT 在现代操作系统中可能更复杂,包含更多的细节。但是,上面的表格给出了一个基本的视图,展示了 IDT 如何为每个中断提供必要的信息。

初始化各种设备

BIOS 接着会初始化连接到计算机的各种硬件设备。例如,VGA 显示器(一种标准的图形显示系统),用于显示启动过程中的信息。

BIOS 然后会初始化 PCI 总线。PCI(外围组件互连标准)是一种连接主板和外围设备的总线标准。它还会初始化它所知道的所有重要设备,比如网络卡、声卡等。

寻找可启动设备

BIOS 完成基本的硬件设置后,接下来的任务是寻找一个可启动的设备。它会按照一定的顺序(通常可以在 BIOS 设置中调整)检查软盘、硬盘、CD-ROM 等设备。当 BIOS 找到一个可启动设备(例如,一个有启动扇区的硬盘)时,它会读取这个设备的引导扇区。引导扇区是存储在存储设备上的一个特殊区域,包含了启动计算机所需的代码。

BIOS 从找到的引导设备中读取引导加载程序(boot loader),并将计算机的控制权转交给它。引导加载程序接下来的任务是加载操作系统。例如,它可能会从硬盘上加载 Windows 或 Linux,并将控制权转交给操作系统。

在这个例子中,BIOS 的角色就像是一个中介,它启动计算机,检查和准备硬件,然后找到并启动引导加载程序,引导加载程序再接着加载整个操作系统。这个过程在每次计算机启动时都会发生,无论是在真实的硬件上,还是在像 QEMU 这样的模拟器中。