Linux0.11 内核引导启动流程

计算机加电后,最开始 BIOS 中的程序会先开始执行,BIOS 程序执行结束后,将由操作系统接管。

引导启动过程中会用到的内核文件说明

boot / bootsect.s : 该程序是磁盘引导块程序,编译后会驻留在磁盘的第一个扇区中(即引导扇区中,0 磁道(柱面),0 磁头,第一个扇区)。计算机机加电 ROM BIOS 自检后,将被 BIOS 加载到内存 0x7C00 处执行。

boot / setup.s : 该程序主要用于读取机器的硬件配置参数,并把内核模块 system 移动到适当的内存位置处。

boot / head.s : 该程序会被编译连接在 system 模块的最前部分,主要进行硬件设备的探测设置和内存管理页面的初始化设置工作。

引导启动过程中文件运行顺序图

引导启动过程中文件运行顺序图

BIOS 程序执行流程

  1. 计算机加电,80x86 结构的 CPU 自动进入实模式,并从地址 0xFFFF0 开始自动执行代码。这个地址也就是 ROM-BIOS 中的地址。
  2. BIOS 执行系统检测,并在物理地址 0 处初始化中断向量。
  3. BIOS 将可启动设备的第一个扇区(磁盘引导扇区,512字节)读入内存绝对地址 0x7C00 处并跳转到这个地方。

接下来将由操作系统文件接管启动流程。

操作系统程序执行流程

Linux 的最最前面部分是用 8086 汇编语言编写的(bootsect.s)。它由 BIOS 读入到内存绝对地址 0x7C00(31KB)处。

  1. bootsect.s 执行时会把自己移动到内存绝对地址 0x90000(576KB)处。
  2. bootsect.s 将启动设备中后 2KB 字节代码(setup.s)读入内存 0x90200 处,将内核代码的其他部分(system 模块)读入从内存地址 0x10000(64KB)开始处。
  3. setup.s 程序将 system 模块移动到物理内存起始位置处,这样 system 模块中代码的地址也即等于实际的物理地址,便于对内核代码和数据进行操作。
  4. setup.s 程序识别主机的某些特性以及 VGA 卡的类型。如果需要,它会要求用户为控制台选择显示模式。然后将整个系统从地址 0x10000 移至 0x0000 处,进入保护模式并跳转至系统的余下部分(0x0000)处。
  5. head.s 程序加载 IDT、GDT 以及 LDT,确认处理器和协处理器,设置分页等。
  6. head.s 调用 main.c 中的 main() 程序。

启动引导时内核在内存中的位置和移动后的位置情况

启动引导时内核在内存中的位置和移动后的位置情况

Issues

  1. bootsect 的代码为什么不把系统模块直接加载到物理地址 0x0000 开始处而要在 setup 程序中再进行移动呢?

    这是因为在随后执行的 setup 代码开始部分还需要利用 ROM BIOS中的中断调用来获取机器的一些参数(例如显示卡模式、硬盘参数表等)。当 BIOS 初始化时会在物理内存开始处放置一个大小为 0x400 字节(1KB)的中断向量表,因此需要在使用完 BIOS 的中断调用后才能将这个区域覆盖掉。

  2. bootsect 程序将 system 模块读入物理地址 0x10000 开始位置处时会不会覆盖在 0x90000(576KB)开始处的内容?

    由于 Linux 0.11 内核 system 模块在当时不会超过 0x80000 字节(即 512KB),所以 bootsect 程序将 system 模块读入物理地址 0x10000 开始位置处时并不会覆盖在 0x90000(576KB)处开始的 bootsect 和 setup 模块。

参考 & 扩展阅读