OS 启动的第一步是 BIOS ,BIOS(Basic Input/Output System,基本输入输出系统)是计算机系统中的一个非常关键的组件,其主要职责可以概括为系统的启动和基础硬件管理。
BIOS 的工作流程和功能
BIOS 的工作流程和功能可以分为以下几个主要方面:
-
开机自检:电脑开机时,BIOS 首先检查电脑的基本硬件是否正常工作,比如内存、键盘等。
-
准备硬件:BIOS 设置并准备好电脑的主要硬件,比如 CPU 和硬盘,为启动操作系统做好准备。
-
引导过程:BIOS 确定从哪个设备(硬盘、U 盘等)启动电脑,并开始加载操作系统。
-
系统设置: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
接下来解释地址 0x000ffff0
由 CS = 0xf000
和IP = 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 这样的模拟器中。