“Boot Sequence”的版本间差异

来自osdev
跳到导航 跳到搜索
第3行: 第3行:
当计算机被打开或重置时,它会运行一系列被称为POST-'''P'''ower-'''O'''n '''S'''elf-'''T'''est的上电自我检测过程。 这一顺序的最终结果是按照固件配置的顺序定位可引导设备,如软盘、CD-ROM或硬盘。
当计算机被打开或重置时,它会运行一系列被称为POST-'''P'''ower-'''O'''n '''S'''elf-'''T'''est的上电自我检测过程。 这一顺序的最终结果是按照固件配置的顺序定位可引导设备,如软盘、CD-ROM或硬盘。


== 主引导记录(Master Boot Record - MRB) ==
== 主引导记录(Master Boot Record - MBR) ==


(传统)BIOS检查可引导设备的引导签名,找到所谓的magic number。 引导签名位于引导扇区(扇区号0)中,它分别在字节偏移量510和511处包含字节序列0x55、0xAA。 当BIOS找到这样一个引导扇区时,它被加载到内存中的<tt>0x0000:0x7c00</tt>(段0,地址0x7c00)。 (然而,一些BIOS加载到<tt>0x7c0:0x0000</tt>(段0x07c0,偏移量0),这解析为相同的物理地址。 一个好的实践是在启动扇区的一开始就强制执行CS:IP。)
(传统)BIOS检查可引导设备的引导签名,找到所谓的magic number。 引导签名位于引导扇区(扇区号0)中,它分别在字节偏移量510和511处包含字节序列0x55、0xAA。 当BIOS找到这样一个引导扇区时,它被加载到内存中的<tt>0x0000:0x7c00</tt>(段0,地址0x7c00)。 (然而,一些BIOS加载到<tt>0x7c0:0x0000</tt>(段0x07c0,偏移量0),这解析为相同的物理地址。 一个好的实践是在启动扇区的一开始就强制执行CS:IP。)
第11行: 第11行:
== 早期运行环境 ==
== 早期运行环境 ==


这种早期执行环境是高度由具体实现定义的,这意味着特定BIOS的实现。 ''永远不''对寄存器的内容做任何假设:它们可能被初始化为0,但也可能包含虚假值。 这包括标志寄存器和SP寄存器,你可能也没有有效的堆栈!唯一可以确定的是,DL寄存器保存了加载引导代码的驱动器代码。
这种早期执行环境是高度由具体实现定义的,这意味着特定BIOS的实现。 ''永远不''对寄存器的内容做任何假设:它们可能被初始化为0,但也可能包含虚假值。 这包括FLAGS寄存器和SP寄存器,你可能也没有有效的堆栈! 唯一可以确定的是,DL寄存器保存了加载引导代码的驱动代码。


CPU当前处于[[Real Mode|实模式]]。 (除非你运行的是一种罕见的BIOS,它认为激活[[Protected Mode|保护模式]]是在帮你的忙。 这意味着你不仅必须编写代码来激活任何“其他”硬件上的保护模式,而且还应为激活的保护模式添加测试条件。)
CPU当前处于[[Real Mode|实模式]]。 (除非你运行的是一种罕见的BIOS,它认为激活[[Protected Mode|保护模式]]是在帮你的忙。 这意味着你不仅必须编写代码来激活任何“其他”硬件上的保护模式,而且还应为激活的保护模式添加测试条件。)
第23行: 第23行:
现在我们知道要加载什么了,让我们看看如何加载。
现在我们知道要加载什么了,让我们看看如何加载。


如果从硬盘引导,则只有446字节可用于引导记录。 查看内核映像运行之前要做的事情列表,你会同意这并不多:
如果从硬盘引导,则只有446字节可用于引导记录。 查看以下内核映像运行之前要做的任务清单,你会发现这点空间并不多:


* 确定从哪个分区启动(通过查找活动分区,或通过向用户提供可供选择的已安装操作系统);
* 确定从哪个分区启动(通过查找活动分区,或通过向用户提供可供选择的已安装操作系统);
第37行: 第37行:
有几种方法可以解决此问题:
有几种方法可以解决此问题:


* '''Geek loading''': 技巧型载入,将上述列表中的所有内容都压缩到引导记录中。 这几乎是不可能的,并且不会为任何特殊情况处理或有用的错误消息留出空间。
* '''Geek loading''': 技巧型载入,将上述列表中的所有内容都压缩到引导记录中。 这几乎是不可能的,并且也许不能为特殊情况处理或有用的错误消息留出空间。
* '''One-stage loading''': 单阶段载入,编写一个用于切换的存根程序,并将其链接到内核映像前面。 引导记录加载内核映像(低于1mb内存标记,因为在实模式下,这是内存上限!),跳入存根(Stub),存根切换到保护模式和运行时准备,再跳入内核。
* '''One-stage loading''': 单阶段载入,编写一个用于切换的存根程序,并将其链接到内核映像前面。 引导记录加载内核映像(低于1mb内存,因为在实模式下,这是内存上限!),跳入存根(Stub),存根切换到保护模式和运行时准备,再跳入内核。
* '''Two-stage loading''': 两阶段载入,编写一个 “单独” 存根程序,该程序加载到1mb内存标记以下,并执行上述列表中的所有操作。
* '''Two-stage loading''': 两阶段载入,编写一个 “单独” 存根程序,该程序加载到1mb内存标记以下,并执行上述列表中的所有操作。


第49行: 第49行:
除非你真的想[[Rolling Your Own Bootloader|弄一个自制引导加载程序]](包含记录record或存根stubs)以获得教学价值,否则我们建议使用现成的[[:Category:bootloaders|引导加载程序]]。
除非你真的想[[Rolling Your Own Bootloader|弄一个自制引导加载程序]](包含记录record或存根stubs)以获得教学价值,否则我们建议使用现成的[[:Category:bootloaders|引导加载程序]]。


最突出的是[[GRUB]],这是一种two-stage bootloader,它不仅提供具有链加载功能的引导菜单,而且将早期环境初始化为定义良好的状态(包括[[Protected Mode|保护模式]],并从BIOS读取各种感兴趣的信息),可以将通用可执行文件作为内核映像加载(而不像大多数其他引导加载程序那样需要纯二进制文件),支持可选的内核模块、各种文件系统,如果<tt>./configure</tt>正确配置还能支持[[Diskless Booting|无盘引导]]。
最突出的是[[GRUB]],这是一种two-stage bootloader,它不仅提供具有链式加载功能的引导菜单,而且将早期环境初始化为定义良好的状态(包括[[Protected Mode|保护模式]],并从BIOS读取各种感兴趣的信息),可以将通用可执行文件作为内核映像加载(而不像大多数其他引导加载程序那样需要简单二进制文件),支持可选的内核模块、各种文件系统,如果<tt>./configure</tt>正确配置还能支持[[Diskless Booting|无盘引导]]。


=== 其他一些方法 ===
=== 其他一些方法 ===

2022年3月5日 (六) 03:21的版本

POST

当计算机被打开或重置时,它会运行一系列被称为POST-Power-On Self-Test的上电自我检测过程。 这一顺序的最终结果是按照固件配置的顺序定位可引导设备,如软盘、CD-ROM或硬盘。

主引导记录(Master Boot Record - MBR)

(传统)BIOS检查可引导设备的引导签名,找到所谓的magic number。 引导签名位于引导扇区(扇区号0)中,它分别在字节偏移量510和511处包含字节序列0x55、0xAA。 当BIOS找到这样一个引导扇区时,它被加载到内存中的0x0000:0x7c00(段0,地址0x7c00)。 (然而,一些BIOS加载到0x7c0:0x0000(段0x07c0,偏移量0),这解析为相同的物理地址。 一个好的实践是在启动扇区的一开始就强制执行CS:IP。)

然后将执行转移到新加载的引导记录(boot record)。 在软盘上,引导记录的所有512字节(保存最后两个签名字节)包含可执行代码。 在硬盘驱动器上,主引导记录(MBR)在偏移量0x0000-0x01bd处保存可执行代码,然后是四个 主分区的表项,每个项使用十六个字节(0x01be-0x01fd)和两个字节的签名(0x01fe-0x01ff)。

早期运行环境

这种早期执行环境是高度由具体实现定义的,这意味着特定BIOS的实现。 永远不对寄存器的内容做任何假设:它们可能被初始化为0,但也可能包含虚假值。 这包括FLAGS寄存器和SP寄存器,你可能也没有有效的堆栈! 唯一可以确定的是,DL寄存器保存了加载引导代码的驱动代码。

CPU当前处于实模式。 (除非你运行的是一种罕见的BIOS,它认为激活保护模式是在帮你的忙。 这意味着你不仅必须编写代码来激活任何“其他”硬件上的保护模式,而且还应为激活的保护模式添加测试条件。)

内核

最后,引导加载程序将内核加载到内存中,并将控制权传递给它。

加载

现在我们知道要加载什么了,让我们看看如何加载。

如果从硬盘引导,则只有446字节可用于引导记录。 查看以下内核映像运行之前要做的任务清单,你会发现这点空间并不多:

  • 确定从哪个分区启动(通过查找活动分区,或通过向用户提供可供选择的已安装操作系统);
  • 确定你的内核映像在引导分区上的位置 (通过解释文件系统或从固定位置加载映像);
  • 将内核映像加载到内存中(需要基本磁盘I/O);
  • 启用保护模式;
  • 为内核准备运行时环境 (例如设置堆栈空间);

你不必按此顺序执行操作,但在调用kmain()之前(译者注:kmain指自制内核的入口函数),必须完成所有这些操作。

更糟糕的是,GCC只生成受保护模式的可执行文件,因此写代码搭建这个早期环境是C做不到的事情之一。

有几种方法可以解决此问题:

  • Geek loading: 技巧型载入,将上述列表中的所有内容都压缩到引导记录中。 这几乎是不可能的,并且也许不能为特殊情况处理或有用的错误消息留出空间。
  • One-stage loading: 单阶段载入,编写一个用于切换的存根程序,并将其链接到内核映像前面。 引导记录加载内核映像(低于1mb内存,因为在实模式下,这是内存上限!),跳入存根(Stub),存根切换到保护模式和运行时准备,再跳入内核。
  • Two-stage loading: 两阶段载入,编写一个 “单独” 存根程序,该程序加载到1mb内存标记以下,并执行上述列表中的所有操作。

传统方式

传统上,MBR将自己重新定位到0x0000:0x0600,从分区表确定活动分区,将该分区的第一个扇区(“分区启动记录”)加载到0x0000:0x7c00,并跳到该地址。 这被称为“链式装载”。如果你希望自己编写的引导记录能够进行双重引导(例如Windows),则应该模拟这种行为。

捷径

除非你真的想弄一个自制引导加载程序(包含记录record或存根stubs)以获得教学价值,否则我们建议使用现成的引导加载程序

最突出的是GRUB,这是一种two-stage bootloader,它不仅提供具有链式加载功能的引导菜单,而且将早期环境初始化为定义良好的状态(包括保护模式,并从BIOS读取各种感兴趣的信息),可以将通用可执行文件作为内核映像加载(而不像大多数其他引导加载程序那样需要简单二进制文件),支持可选的内核模块、各种文件系统,如果./configure正确配置还能支持无盘引导

其他一些方法

有许多可能的变体可以引导系统。以下是方法列表,但可能还有更多方法:

  • 你可以使用一个未使用的分区并加载阶段二的原始映像(raw)
  • 你可以将阶段二放在MBR和第一个分区开头之间
  • 你可以(像Lilo那样)编写一个内核文件,然后使用工具检测扇区(或簇)。然后让阶段一从列表中加载扇区。
  • DOS和Windows是这样做的: 创建一个空文件系统 (对其进行格式化),然后将内核放置在第一个文件中,将shell放置在空根目录的第二个文件中。 因此,加载程序只需在根目录中加载第一个条目,然后再加载第二个条目。
  • 旧Linux是从软盘启动的。 第一个扇区(“boot”)以“原始内容”(不带文件系统)模式加载第二个阶段 (第二阶段是 “setup”,位于 “boot” 后面的扇区中) 第二阶段确实设置了系统(视频模式、内存映射等),然后加载真实的内核映像(打包在tgz/bz中)。
  • 几年前,有一个引导加载程序(称为“nuni”),它能切换到保护模式并加载一个文件,同时把所有这些都实现在一个引导扇区中

另见

外部链接

 de:Bootprozess auf x86