“Boot Sequence”的版本间差异

来自osdev
跳到导航 跳到搜索
(创建页面,内容为“== POST == 当计算机被打开或重置时,它会运行一系列被称为POST-“P”ower-“O”n-“elf-“T”est的诊断。 这一顺序的最终结果是按照固件配置的顺序定位可引导设备,如软盘、CD-ROM或硬盘。 == 主引导记录 == (传统)BIOS检查可引导设备的引导签名,即所谓的幻数。 引导签名位于引导扇区(扇区号0)中,它分别在字节偏移量510和511处包含字节序列0x55、0x…”)
 
第1行: 第1行:
== POST ==
== POST ==


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


== 主引导记录 ==
== 主引导记录 ==


(传统)BIOS检查可引导设备的引导签名,即所谓的幻数。 引导签名位于引导扇区(扇区号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。)


然后将执行转移到新加载的引导记录。 在软盘上,引导记录的所有512字节(保存最后两个签名字节)可能包含可执行代码。 在硬盘驱动器上,[[MBR(x86)|主引导记录]](MBR)在偏移量0x0000-0x01bd处保存可执行代码,然后是四个[[Partition table | primary partitions]]的表项,每个项使用十六个字节(0x01be-0x01fd)和两个字节的签名(0x01fe-0x01ff)。
然后将执行转移到新加载的引导记录。 在软盘上,引导记录的所有512字节(保存最后两个签名字节)可能包含可执行代码。 在硬盘驱动器上,[[MBR(x86)|主引导记录]](MBR)在偏移量0x0000-0x01bd处保存可执行代码,然后是四个[[Partition table | primary partitions]]的表项,每个项使用十六个字节(0x01be-0x01fd)和两个字节的签名(0x01fe-0x01ff)。
第11行: 第11行:
== 早期环境 ==
== 早期环境 ==


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


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


== 内核 ==
== 内核 ==
第23行: 第23行:
现在我们知道要加载什么了,让我们看看如何加载。
现在我们知道要加载什么了,让我们看看如何加载。


如果从硬盘引导,则只有446字节可用于引导记录。 查看内核映像运行之前要做的事情列表,您会同意这并不多:
如果从硬盘引导,则只有446字节可用于引导记录。 查看内核映像运行之前要做的事情列表,你会同意这并不多:


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


您不必按此顺序执行操作,但在调用<tt>kmain()</tt>之前,必须完成所有这些操作。
你不必按此顺序执行操作,但在调用<tt>kmain()</tt>之前,必须完成所有这些操作。


更糟糕的是,GCC只生成受保护模式的可执行文件,因此这个早期环境的代码是[[C#C做不到的事情| C做不到的事情]]之一。
更糟糕的是,GCC只生成受保护模式的可执行文件,因此这个早期环境的代码是[[C#Things C can't do|C做不到的事情]]之一。


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


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


=== 传统方式 ===
=== 传统方式 ===


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


=== 捷径 ===
=== 捷径 ===


除非您真的想[[滚动您自己的引导加载程序]](记录/存根)以获得教育价值,否则我们建议使用现成的[[:类别:引导加载程序|引导加载程序]]。
除非你真的想[[Rolling Your Own Bootloader|弄一个自制引导加载程序]](记录/存根)以获得教育价值,否则我们建议使用现成的[[:Category:bootloaders|引导加载程序]]。


最突出的是[[GRUB]],这是一种两级引导加载程序,它不仅提供具有链加载功能的引导菜单,而且将早期环境初始化为定义良好的状态(包括[[Protected Mode]],并从BIOS读取各种感兴趣的信息),可以将通用可执行文件作为内核映像加载(而不像大多数其他引导加载程序那样需要平面二进制文件),支持可选的内核模块、各种文件系统以及if<tt>/正确配置,[[Diskless Booting]]。
最突出的是[[GRUB]],这是一种两级引导加载程序,它不仅提供具有链加载功能的引导菜单,而且将早期环境初始化为定义良好的状态(包括[[Protected Mode]],并从BIOS读取各种感兴趣的信息),可以将通用可执行文件作为内核映像加载(而不像大多数其他引导加载程序那样需要平面二进制文件),支持可选的内核模块、各种文件系统以及if<tt>/正确配置,[[Diskless Booting]]。
第53行: 第53行:
=== 一些方法 ===
=== 一些方法 ===
有许多可能的变体可以引导。以下是方法列表,但可能还有更多方法:
有许多可能的变体可以引导。以下是方法列表,但可能还有更多方法:
*您可以使用一个未使用的分区并加载阶段2“raw”
* 你可以使用一个未使用的分区并加载阶段2“raw”
*您可以将阶段2放在MBR和第一个分区的开始之间
* 你可以将阶段2放在MBR和第一个分区的开始之间
*您可以(像Lilo那样)编写一个内核文件,然后使用工具检测扇区(或集群)。然后让阶段1从列表中加载扇区。
* 你可以(像Lilo那样)编写一个内核文件,然后使用工具检测扇区(或集群)。然后让阶段1从列表中加载扇区。
*DOS和Windows是这样做的:创建一个空文件系统(格式化),然后将内核放在第一个文件中,将shell放在第二个文件中的空rootdir中。因此,加载程序只需加载rootdir中的第一个条目,然后加载第二个条目。
* DOS和Windows是这样做的:创建一个空文件系统(格式化),然后将内核放在第一个文件中,将shell放在第二个文件中的空rootdir中。因此,加载程序只需加载rootdir中的第一个条目,然后加载第二个条目。
*旧Linux是从软盘引导的。第一个扇区(“boot”)以“raw”模式加载第二个阶段=不带文件系统(scond阶段是“setup”,在“boot”后面的扇区中)。第二个阶段设置了系统(视频模式、内存映射等),然后加载了真正的内核映像(打包在tgz/bz中)。
* 旧Linux是从软盘引导的。第一个扇区(“boot”)以“raw”模式加载第二个阶段=不带文件系统(scond阶段是“setup”,在“boot”后面的扇区中)。第二个阶段设置了系统(视频模式、内存映射等),然后加载了真正的内核映像(打包在tgz/bz中)。
*几年前,这里有一个引导加载程序(称为“nuni”),它切换到pmode并加载一个文件,所有这些都在一个引导扇区中
* 几年前,有一个引导加载程序(称为“nuni”),它切换到pmode并加载一个文件,所有这些都在一个引导扇区中


== See Also ==
== 另见 ==


=== 外部链接 ===
=== 外部链接 ===

2022年3月5日 (六) 02:22的版本

POST

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

主引导记录

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

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

早期环境

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

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

内核

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

加载

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

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

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

你不必按此顺序执行操作,但在调用kmain()之前,必须完成所有这些操作。

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

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

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

传统方式

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

捷径

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

最突出的是GRUB,这是一种两级引导加载程序,它不仅提供具有链加载功能的引导菜单,而且将早期环境初始化为定义良好的状态(包括Protected Mode,并从BIOS读取各种感兴趣的信息),可以将通用可执行文件作为内核映像加载(而不像大多数其他引导加载程序那样需要平面二进制文件),支持可选的内核模块、各种文件系统以及if/正确配置,Diskless Booting

一些方法

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

  • 你可以使用一个未使用的分区并加载阶段2“raw”
  • 你可以将阶段2放在MBR和第一个分区的开始之间
  • 你可以(像Lilo那样)编写一个内核文件,然后使用工具检测扇区(或集群)。然后让阶段1从列表中加载扇区。
  • DOS和Windows是这样做的:创建一个空文件系统(格式化),然后将内核放在第一个文件中,将shell放在第二个文件中的空rootdir中。因此,加载程序只需加载rootdir中的第一个条目,然后加载第二个条目。
  • 旧Linux是从软盘引导的。第一个扇区(“boot”)以“raw”模式加载第二个阶段=不带文件系统(scond阶段是“setup”,在“boot”后面的扇区中)。第二个阶段设置了系统(视频模式、内存映射等),然后加载了真正的内核映像(打包在tgz/bz中)。
  • 几年前,有一个引导加载程序(称为“nuni”),它切换到pmode并加载一个文件,所有这些都在一个引导扇区中

另见

外部链接

 de:Bootprozess auf x86