第 15 章 FreeBSD 启动过程

15.1. 概要

启动计算机并加载操作系统的过程称为“引导过程”或“启动”。FreeBSD 的引导过程提供了极大的灵活性,可以自定义系统启动时发生的事情,包括从同一台计算机上安装的不同操作系统、同一操作系统的不同版本或不同的已安装内核中进行选择。

本章详细介绍了可以设置的配置选项。它演示了如何自定义 FreeBSD 引导过程,包括从发生到 FreeBSD 内核启动、探测设备并启动 init(8) 的所有内容。当引导消息的文本颜色从亮白色变为灰色时,就会发生这种情况。

阅读完本章后,您将了解

  • FreeBSD 引导系统的组件及其交互方式。

  • 可以传递给 FreeBSD 引导程序中的组件的选项,以便控制引导过程。

  • 设备提示的基本设置。

  • 如何启动到单用户和多用户模式,以及如何正确关闭 FreeBSD 系统。

本章仅描述在 x86 和 amd64 系统上运行的 FreeBSD 的引导过程。

15.2. FreeBSD 引导过程

打开计算机并启动操作系统提出了一个有趣的难题。根据定义,在操作系统启动之前,计算机不知道如何执行任何操作。这包括从磁盘运行程序。如果计算机在没有操作系统的情况下无法从磁盘运行程序,而操作系统程序位于磁盘上,那么操作系统是如何启动的呢?

此问题与《男爵漫游记》一书中的一个情节相似。一个角色掉进了下水道里,并通过抓住自己的靴带把自己拉了出来。在计算机的早期,术语“引导”被应用于加载操作系统的机制。此后它被缩短为“启动”。

在 x86 硬件上,基本输入/输出系统 (BIOS) 负责加载操作系统。BIOS 在硬盘上查找主引导记录 (MBR),该记录必须位于磁盘上的特定位置。BIOS 具有足够的知识来加载和运行 MBR,并假定 MBR 然后可以执行加载操作系统所涉及的其余任务,可能在 BIOS 的帮助下。

FreeBSD 支持从较旧的 MBR 标准和较新的 GUID 分区表 (GPT) 启动。GPT 分区通常在具有统一可扩展固件接口 (UEFI) 的计算机上找到。但是,FreeBSD 即使在只有传统 BIOS 的机器上也可以使用 gptboot(8) 从 GPT 分区启动。正在努力提供直接的 UEFI 启动。

MBR 中的代码通常称为引导管理器,尤其是在它与用户交互时。引导管理器通常在磁盘的第一磁道或文件系统中具有更多代码。引导管理器的示例包括标准的 FreeBSD 引导管理器 boot0(也称为 Boot Easy)和 GNU GRUB,后者被许多 Linux® 发行版使用。

GRUB 用户应参考 GNU 提供的文档

如果只安装了一个操作系统,MBR 会搜索磁盘上的第一个可引导(活动)分区,然后运行该分区上的代码以加载操作系统的其余部分。当存在多个操作系统时,可以安装不同的引导管理器来显示操作系统列表,以便用户可以选择一个操作系统进行启动。

FreeBSD 引导系统的其余部分分为三个阶段。第一阶段只知道足够的信息来将计算机置于特定状态并运行第二阶段。第二阶段可以做更多一点的事情,然后再运行第三阶段。第三阶段完成加载操作系统的任务。将工作分成三个阶段是因为 MBR 对第一阶段和第二阶段可以运行的程序的大小有限制。将任务链接在一起允许 FreeBSD 提供更灵活的加载程序。

然后内核启动并开始探测设备并初始化它们以供使用。内核引导过程完成后,内核将控制权传递给用户进程 init(8),它确保磁盘处于可用状态,启动用户级资源配置(挂载文件系统),设置网络卡以在网络上通信,并启动已配置在启动时运行的进程。

本节将更详细地描述这些阶段,并演示如何与 FreeBSD 引导过程交互。

15.2.1. 引导管理器

MBR 中的引导管理器代码有时被称为引导过程的第零阶段。默认情况下,FreeBSD 使用 boot0 引导管理器。

FreeBSD 安装程序安装的 MBR 基于 /boot/boot0。由于分区的表和 MBR 末尾的0x55AA标识符,boot0 的大小和功能限制为 446 字节。如果安装了 boot0 和多个操作系统,则在启动时将显示类似以下示例的消息

示例 1. boot0 屏幕截图
F1 Win
F2 FreeBSD

Default: F2

如果在安装 FreeBSD 后安装了其他操作系统,它们将覆盖现有的 MBR。如果发生这种情况,或者要将现有的 MBR 替换为 FreeBSD MBR,请使用以下命令

# fdisk -B -b /boot/boot0 device

其中device是引导磁盘,例如 ad0 表示第一个 IDE 磁盘,ad2 表示第二个 IDE 控制器上的第一个 IDE 磁盘,或 da0 表示第一个 SCSI 磁盘。要创建 MBR 的自定义配置,请参考 boot0cfg(8)

15.2.2. 第一阶段和第二阶段

从概念上讲,第一阶段和第二阶段是磁盘同一区域上同一程序的一部分。由于空间限制,它们被分成两个,但始终一起安装。它们由 FreeBSD 安装程序或bsdlabel从组合的 /boot/boot 中复制。

这两个阶段位于文件系统外部,在引导分区的第一个磁道开始,从第一个扇区开始。这是 boot0 或任何其他引导管理器期望找到一个程序来运行以继续引导过程的地方。

第一阶段,boot1,非常简单,因为它的大小只能为 512 字节。它只了解足够的 FreeBSD bsdlabel(存储有关分区的的信息)来查找并执行 boot2

第二阶段,boot2,稍微复杂一些,并且足够了解 FreeBSD 文件系统以找到文件。它可以提供一个简单的界面来选择要运行的内核或加载程序。它运行加载程序,后者更复杂,并提供一个引导配置文件。如果引导过程在第二阶段中断,则会显示以下交互式屏幕

示例 2. boot2 屏幕截图
>> FreeBSD/i386 BOOT
Default: 0:ad(0,a)/boot/loader
boot:

要替换已安装的 boot1boot2,请使用bsdlabel,其中diskslice是要从中引导的磁盘和分区,例如 ad0s1 表示第一个 IDE 磁盘上的第一个分区

# bsdlabel -B diskslice

如果只使用磁盘名称,例如 ad0,则bsdlabel将创建磁盘为“危险的专用模式”,没有分区。这可能不是所需的操作,因此在按 Return 之前请仔细检查diskslice

15.2.3. 第三阶段

加载程序是三阶段引导过程的最后阶段。它位于文件系统上,通常为 /boot/loader

加载程序旨在作为一种交互式配置方法,使用内置命令集,并由一个更强大的解释器支持,该解释器具有更复杂的命令集。

在初始化期间,加载程序将探测控制台和磁盘,并确定它正在从哪个磁盘引导。它将相应地设置变量,并启动一个解释器,用户命令可以从脚本或交互式地传递。

然后加载程序将读取 /boot/loader.rc,默认情况下,该文件读取 /boot/defaults/loader.conf,后者为变量设置合理的默认值,并读取 /boot/loader.conf 以进行对这些变量的本地更改。loader.rc 然后根据这些变量采取行动,加载选定的模块和内核。

最后,默认情况下,加载程序会等待 10 秒钟以查看是否有按键按下,如果未中断则启动内核。如果中断,则向用户显示一个了解命令集的提示,用户可以在其中调整变量、卸载所有模块、加载模块,然后最后启动或重新启动。加载程序内置命令 列出了最常用的加载程序命令。有关所有可用命令的完整讨论,请参考 loader(8)

表 1. 加载程序内置命令
变量描述

autoboot seconds

如果在给定的时间段内(以秒为单位)未中断,则继续启动内核。它会显示倒计时,默认时间段为 10 秒。

boot [-options] [kernelname]

立即启动内核,并使用任何指定的选项或内核名称。仅在发出unload命令后,才能在命令行中提供内核名称。否则,将使用先前加载的内核。如果kernelname未限定,则将在/boot/kernel/boot/modules下搜索。

boot-conf

根据指定的变量(最常见的是kernel)执行相同的模块自动配置。只有在先使用unload更改某些变量后,此操作才有意义。

help [topic]

显示从/boot/loader.help读取的帮助信息。如果给定的主题为index,则会显示可用主题的列表。

include filename …​

读取指定的文件并逐行解释。任何错误都会立即停止include操作。

load [-t type] filename

加载指定文件名的内核、内核模块或指定类型的文件。filename之后的任何参数都将传递给该文件。如果filename未限定,则将在/boot/kernel/boot/modules下搜索。

ls [-l] [path]

显示给定路径中的文件列表,如果未指定路径,则显示根目录中的文件列表。如果指定了-l,则还会显示文件大小。

lsdev [-v]

列出所有可能从中加载模块的设备。如果指定了-v,则会打印更多详细信息。

lsmod [-v]

显示已加载的模块。如果指定了-v,则会显示更多详细信息。

more filename

显示指定的文件,并在显示每个LINES行时暂停。

reboot

立即重启系统。

set variable, set variable=value

设置指定的环境变量。

unload

移除所有已加载的模块。

以下是一些加载程序用法的实际示例。要以单用户模式启动常用内核

 boot -s

要卸载常用内核和模块,然后加载先前或其他指定的内核

 unload
 load /path/to/kernelfile

使用限定的/boot/GENERIC/kernel来引用安装附带的默认内核,或使用/boot/kernel.old/kernel来引用系统升级或配置自定义内核之前的先前安装的内核。

使用以下命令加载另一个内核的常用模块。请注意,在这种情况下,不需要限定名称

unload
set kernel="mykernel"
boot-conf

要加载自动内核配置脚本

 load -t userconfig_script /boot/kernel.conf

15.2.4. 最后阶段

内核由加载程序或boot2(绕过加载程序)加载后,它会检查任何引导标志并根据需要调整其行为。启动期间的内核交互列出了常用的引导标志。有关其他引导标志的更多信息,请参阅boot(8)

表 2. 启动期间的内核交互
选项描述

-a

在内核初始化期间,询问要挂载为根文件系统的设备。

-C

从 CDROM 启动根文件系统。

-s

启动到单用户模式。

-v

在内核启动期间更详细地输出信息。

内核完成启动后,它将控制权传递给用户进程init(8),该进程位于/sbin/init,或loaderinit_path变量中指定的程序路径。这是引导过程的最后阶段。

引导序列确保系统上可用的文件系统一致。如果UFS文件系统不一致,并且fsck无法修复不一致性,则init会将系统置于单用户模式,以便系统管理员可以直接解决问题。否则,系统将引导到多用户模式。

15.2.4.1. 单用户模式

用户可以通过使用-s启动或在加载程序中设置boot_single变量来指定此模式。还可以通过从多用户模式运行shutdown now来进入此模式。单用户模式以以下消息开头

Enter full pathname of shell or RETURN for /bin/sh:

如果用户按下Enter键,系统将进入默认的Bourne shell。要指定其他shell,请输入shell的完整路径。

单用户模式通常用于修复由于文件系统不一致或引导配置文件错误而无法引导的系统。当不知道root密码时,它也可用于重置root密码。这些操作之所以成为可能,是因为单用户模式提示符提供了对系统及其配置文件的完全本地访问权限。此模式下没有网络连接。

虽然单用户模式对于修复系统很有用,但除非系统位于物理安全位置,否则它会带来安全风险。默认情况下,任何可以获得系统物理访问权限的用户在引导到单用户模式后都将完全控制该系统。

如果在/etc/ttys中将系统console更改为insecure,则系统将在启动单用户模式之前首先提示输入root密码。这在增强安全性的同时,也消除了在不知道root密码时重置密码的功能。

示例 3. 在/etc/ttys中配置不安全的控制台
# name  getty                           type    status          comments
#
# If console is marked "insecure", then init will ask for the root password
# when going to single-user mode.
console none                            unknown off insecure

insecure控制台意味着控制台的物理安全性被认为是不安全的,因此只有知道root密码的人才能使用单用户模式。

15.2.4.2. 多用户模式

如果init发现文件系统处于正常状态,或者用户在单用户模式下完成其命令并键入exit退出单用户模式后,系统将进入多用户模式,在此模式下它将启动系统的资源配置。

资源配置系统从/etc/defaults/rc.conf读取配置默认值,并从/etc/rc.conf读取特定于系统的详细信息。然后,它继续挂载/etc/fstab中列出的系统文件系统。它启动网络服务、各种系统守护进程,然后启动本地安装的软件包的启动脚本。

要了解有关资源配置系统的更多信息,请参阅rc(8)并检查位于/etc/rc.d中的脚本。

15.3. 设备提示

在初始系统启动期间,引导loader(8)读取device.hints(5)。此文件存储称为变量的内核引导信息,有时也称为“设备提示”。设备驱动程序使用这些“设备提示”进行设备配置。

也可以在第 3 阶段引导加载程序提示符处指定设备提示,如第三阶段中所示。可以使用set添加变量,使用unset删除变量,并使用show查看变量。/boot/device.hints中设置的变量也可以被覆盖。在引导加载程序中输入的设备提示不是永久性的,并且不会在下次重启时应用。

系统启动后,可以使用kenv(1)转储所有变量。

/boot/device.hints的语法是每行一个变量,使用井号“#”作为注释标记。行的构造如下

 hint.driver.unit.keyword="value"

第 3 阶段引导加载程序的语法为

 set hint.driver.unit.keyword=value

其中driver是设备驱动程序名称,unit是设备驱动程序单元号,keyword是提示关键字。关键字可以包含以下选项

  • at:指定设备连接到的总线。

  • port:指定要使用的I/O的起始地址。

  • irq:指定要使用的中断请求号。

  • drq:指定DMA通道号。

  • maddr:指定设备占用的物理内存地址。

  • flags:设置设备的各种标志位。

  • disabled:如果设置为1,则禁用设备。

由于设备驱动程序可能会接受或需要此处未列出的更多提示,因此建议查看驱动程序的手册页。有关更多信息,请参阅device.hints(5)kenv(1)loader.conf(5)loader(8)

15.4. 关机序列

使用shutdown(8)进行受控关机时,init(8)将尝试运行脚本/etc/rc.shutdown,然后继续向所有进程发送TERM信号,随后向任何未及时终止的进程发送KILL信号。

要在支持电源管理的架构和系统上的FreeBSD机器上关闭电源,请使用shutdown -p now立即关闭电源。要重启FreeBSD系统,请使用shutdown -r now。必须是root用户或operator组的成员才能运行shutdown(8)。还可以使用halt(8)reboot(8)。有关更多信息,请参阅其手册页和shutdown(8)

有关修改组成员身份的信息,请参阅“用户和基本帐户管理”

电源管理需要将acpi(4)加载为模块或静态编译到自定义内核中。


上次修改时间:2024年9月23日,作者:Fernando Apesteguía