第十五章 FreeBSD启动过程15.1. 简介15.2. FreeBSD启动过程15.2.1. 启动管理器15.2.2. 第一阶段和第二阶段15.2.3. 第三阶段15.2.4. 最后阶段15.2.4.1. 单用户模式15.2.4.2. 多用户模式15.3. 设备提示15.4. 停机顺序
启动计算机并加载操作系统的过程称为“引导过程”(the bootstrap process)或“引导”(booting)。FreeBSD的引导过程在定制系统启动时发生的事情方面提供了很大的灵活性,包括从安装在同一台计算机上的不同操作系统、同一操作系统的不同版本或安装的不同内核中进行选择的能力。
本章详细介绍了可以设置的配置选项。它演示了如何自定义FreeBSD引导过程,包括在FreeBSD内核启动、探测设备和启动 init(8) 之前发生的一切。当启动信息的文本颜色从亮白色变为灰色时,就会发生这种情况。
阅读本章后,您将认识到:
本章仅描述在x86和amd64系统上运行的FreeBSD的引导过程。
打开电脑并启动操作系统会带来一个有趣的困境。根据定义,在操作系统启动之前,计算机不知道如何做任何事情。这包括从磁盘运行程序。如果计算机在没有操作系统的情况下无法从磁盘运行程序,并且操作系统程序在磁盘上,那么操作系统是如何启动的?
这个问题与《蒙乔森男爵历险记》(The Adventures of Baron Munchausen)一书中的一个问题相似。一个角色从沙井里掉了一半,抓住他的鞋带(bootstraps)并举起来把自己拉了出来。在计算的早期,bootstraps 一词被应用于加载操作系统的机制。此后,它被缩短为 booting 。
在x86硬件上,基本输入/输出系统(BIOS)负责加载操作系统。BIOS在硬盘上查找主引导记录(MBR),该记录必须位于磁盘上的特定位置。BIOS有足够的知识来加载和运行MBR,并假设MBR可以在BIOS的帮助下执行加载操作系统所涉及的其余任务。
xxxxxxxxxxFreeBSD提供从较旧的MBR标准和较新的GUID分区表(GPT)启动。GPT分区通常出现在具有统一可扩展固件接口(UEFI)的计算机上。然而,FreeBSD可以从GPT分区启动,即使在只有gptboot(8)旧BIOS的机器上也是如此。提供直接UEFI引导的工作正在进行中。MBR中的代码通常被称为引导管理器(boot manager),特别是当它与用户交互时。引导管理器通常在磁盘的第一个轨道或文件系统中有更多的代码。引导管理器的示例包括标准的FreeBSD引导管理器 boot0 ,也称为 Boot Easy ,以及许多Linux®发行版使用的 GNU GRUB 。
xxxxxxxxxxGRUB的用户应该参考GNU提供的文档。如果只安装了一个操作系统,MBR会搜索磁盘上的第一个可引导(活动)切片,然后在该切片上运行代码以加载操作系统的其余部分。当存在多个操作系统时,可以安装不同的引导管理器来显示操作系统列表,以便用户可以选择一个进行引导。
FreeBSD引导系统的其余部分分为三个阶段。第一阶段只知道让计算机进入特定状态并运行第二阶段。在运行第三阶段之前,第二阶段可以做得更多。第三阶段完成加载操作系统的任务。这项工作分为三个阶段,因为MBR限制了可以在第一和第二阶段运行的程序的大小。通过将任务链接在一起,FreeBSD可以提供更灵活的加载器。
然后启动内核,开始探测设备并初始化它们以供使用。一旦内核引导过程完成,内核将控制权传递给用户进程 init(8) ,该进程确保磁盘处于可用状态,启动用户级资源配置,挂载文件系统,设置网卡在网络上通信,并启动已配置为在启动时运行的进程。
本节将更详细地描述这些阶段,并演示如何与FreeBSD引导过程交互。
MBR中的引导管理器代码有时被称为引导过程的零阶段(stage zero)。默认情况下,FreeBSD使用boot0引导管理器。
FreeBSD安装程序安装的MBR基于 /boot/boot0 。由于MBR末尾的切片表和0x55AA标识符,boot0的大小和容量限制为446字节。如果安装了boot0和多个操作系统,则在启动时将显示类似于此示例的消息:
xxxxxxxxxxF1 WinF2 FreeBSDDefault: F2如果其他操作系统是在FreeBSD之后安装的,它们将覆盖现有的MBR。如果发生这种情况,或者要用FreeBSD MBR替换现有的MBR,请使用以下命令:
xxxxxxxxxx# fdisk -B -b /boot/boot0 device其中设备是引导磁盘,例如ad0表示第一个IDE磁盘,ad2表示第二个IDE控制器上的第一个IDE硬盘,da0表示第一块SCSI磁盘。要创建MBR的自定义配置,请参阅 boot0cfg(8) 。
从概念上讲,第一和第二阶段是磁盘同一区域上同一程序的一部分。由于空间限制,它们被一分为二,但始终安装在一起。它们是由FreeBSD安装程序或 bsdlabel 从组合的 /boot/boot 中复制的。
这两个阶段位于文件系统之外,在引导片的第一个轨道上,从第一个扇区开始。这就是boot0或任何其他引导管理器希望找到一个程序来运行的地方,该程序将继续引导过程。
第一阶段boot1非常简单,因为它的大小只能是512字节。它对存储切片信息的FreeBSD bsdlabel了解得足够多,可以找到并执行boot2。
第二阶段boot2稍微复杂一些,对FreeBSD文件系统有足够的了解,可以找到文件。它可以提供一个简单的界面来选择要运行的内核或加载器。它运行加载器,加载器更复杂,并提供引导配置文件。如果启动过程在第二阶段中断,将显示以下交互式屏幕:
示例24. boot2 截屏
xxxxxxxxxx>> FreeBSD/i386 BOOTDefault: 0:ad(0,a)/boot/loaderboot:要替换已安装的boot1和boot2,请使用 bsdlabel ,其中 diskslice 是要启动的磁盘和切片,例如ad0s1是第一个IDE磁盘上的第一个切片:
xxxxxxxxxx# bsdlabel -B diskslicexxxxxxxxxx如果只使用磁盘名称,如ad0,bsdlabel将以“危险专用模式”创建磁盘,而不使用切片。这可能不是所需的操作,因此在按Return键之前,请仔细检查磁 diskslice。加载器是三阶段引导过程的最后阶段。它位于文件系统上,通常为 /boot/loader 。
加载器旨在作为一种交互式配置方法,使用内置命令集,由功能更强大的解释器备份,解释器具有更复杂的命令集。
在初始化过程中,加载器将探测控制台和磁盘,并找出它从哪个磁盘启动。它将相应地设置变量,并启动解释器,用户命令可以从脚本传递或交互式传递。
然后,加载器将读取 /boot/loader.rc ,默认情况下,它会读取 /boot/defaults/loader.conf ,后者为变量设置合理的默认值,并读取 /boot/loder.conf 以对这些变量进行本地更改。 loader.rc 然后对这些变量进行操作,加载所选的任何模块和内核。
最后,默认情况下,loader会等待10秒按键,如果内核没有中断,则会启动内核。如果中断,用户将看到一个理解命令集的提示,用户可以在其中调整变量、卸载所有模块、加载模块,然后最终启动或重新启动。加载器内置命令列出了最常用的加载器命令。有关所有可用命令的完整讨论,请参阅 loader(8) 。
表28. 加载器内置命令
| 变量 | 描述 |
|---|---|
| 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 | 使用指定的文件名加载给定类型的内核、内核模块或文件。文件名后的任何参数都会传递给文件。如果文件名不合格,将在 /boot/kernel 和 /boot/modules 下搜索。 |
| ls [-l] [path] | 显示给定路径或根目录(如果未指定路径)中的文件列表。如果指定了 -l ,还将显示文件大小。 |
| lsdev [-v] | 列出可能加载模块的所有设备。如果指定了 -v ,则会打印更多详细信息。 |
| lsmod [-v] | 显示加载的模块。如果指定了 -v ,将显示更多详细信息。 |
| more filename | 显示指定的文件,并在每个显示的 LINES 处暂停。 |
| reboot | 立即重新启动系统。 |
| set variable, set variable=value | 设置指定的环境变量。 |
| unload | 删除所有加载的模块。 |
以下是一些加载器使用的实际示例。要在单用户模式下启动通常的内核:
xxxxxxxxxxboot -s要卸载通常的内核和模块,然后加载上一个或另一个指定的内核:
xxxxxxxxxxunloadload /path/to/kernelfile使用限定的 /boot/GENERIC/kernel 来指代安装附带的默认内核,或使用 /boot/kernel.old/kernel 在系统升级或配置自定义内核之前指代以前安装的内核。
使用以下命令将常用模块加载到另一个内核中。请注意,在这种情况下,不需要限定名:
xxxxxxxxxxunloadset kernel="mykernel"boot-conf要加载自动内核配置脚本,请执行以下操作:
xxxxxxxxxxload -t userconfig_script /boot/kernel.conf一旦内核被加载器或绕过加载器的boot2加载,它就会检查任何引导标志,并根据需要调整其行为。【表29. 引导期间的内核交互】列出了常用的引导标志。有关其他启动标志的更多信息,请参阅 boot(8) 。
表29. 引导期间的内核交互
| 选项 | 描述 |
|---|---|
| -a | 在内核初始化期间,要求将设备作为根文件系统挂载。 |
| -C | 从CDROM启动根文件系统。 |
| -s | 启动到单用户模式。 |
| -v | 在内核启动期间更加详细。 |
一旦内核完成引导,它就会将控制权传递给用户进程 init(8) ,该进程位于 /sbin/init ,或 loader 中 init_path 变量中指定的程序路径。这是引导过程的最后阶段。
引导顺序确保系统上可用的文件系统是一致的。如果UFS文件系统不是,并且 fsck 无法修复不一致,init会将系统切换到单用户模式,以便系统管理员可以直接解决问题。否则,系统将启动到多用户模式。
用户可以通过使用 -s 启动或在加载器中设置 boot_single 变量来指定此模式。也可以通过从多用户模式运行关机来实现。单用户模式以以下消息开头:
xxxxxxxxxxEnter full pathname of shell or RETURN for /bin/sh:如果用户按 Enter 键,系统将进入默认的Bourne shell。要指定其他shell,请输入shell的完整路径。
单用户模式通常用于修复因文件系统不一致或启动配置文件错误而无法启动的系统。它还可以用于在未知时重置根密码。这些操作是可能的,因为单用户模式提示可以完全本地访问系统及其配置文件。此模式下没有网络。
虽然单用户模式对于修复系统很有用,但除非系统位于物理安全的位置,否则它会带来安全风险。默认情况下,任何可以物理访问系统的用户在启动到单用户模式后都可以完全控制该系统。
如果系统console(控制台)在 /etc/ttys 中更改为 insecure (不安全),系统将在启动单用户模式之前首先提示输入 root 密码。这增加了一种安全措施,同时删除了在未知时重置 root 密码的能力。
示例25. 在/etc/ttys中配置不安全的控制台
xxxxxxxxxx# 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 insecureinsecure (不安全的)控制台意味着控制台的物理安全被认为是不安全的,因此只有知 道 root 密码的人才能使用单用户模式。
如果init发现文件系统正常,或者一旦用户在单用户模式下完成命令并键入 exit 离开单用户模式,系统就会进入多用户模式,在该模式下开始系统的资源配置。
资源配置系统从 /etc/defaults/rc.conf 读取配置默认值,从 /etc/rc.conf 读取系统特定的详细信息。然后,它继续挂载 /etc/fstab 中列出的系统文件系统。它启动网络服务、各种系统守护进程,然后启动本地安装的软件包的启动脚本。
要了解有关资源配置系统的更多信息,请参阅 rc(8) 并检查位于 /etc/rc.d 中的脚本。
在初始系统启动期间,引导 loader(8) 读取 device.hints(5) 。此文件存储称为变量的内核引导信息,有时也称为“设备提示(device hints)”。这些“设备提示”由设备驱动程序用于设备配置。
设备提示也可以在第三阶段引导加载程序提示符处指定,如【15.2.3. 第三阶段】所示。变量可以使用 set 添加,使用 unset 删除,以及使用 show 查看。在 /boot/device.hints 中设置的变量也可以被覆盖。在引导加载程序中输入的设备提示不是永久性的,不会在下次重新启动时应用。
系统启动后,可以使用 kenv(1) 转储所有变量。
/boot/device.hints 的语法是每行一个变量,使用哈希 # 作为注释标记。行的结构如下:
xxxxxxxxxxhint.driver.unit.keyword="value"Stage 3引导加载程序的语法为:
xxxxxxxxxxset hint.driver.unit.keyword=value其中 driver 是设备驱动程序名称,unit 是设备驱动单元编号,keyword 是提示关键字。关键字可能包含以下选项:
1 ,则设备被禁用。由于设备驱动程序可能接受或需要此处未列出的更多提示,建议查看驱动程序手册页面。有关更多信息,请参阅 device.hints(5) 、kenv(1) 、loader.conf(5) 和loader(8) 。
在使用 shutdown(8) 进行受控关机后, init(8) 将尝试运行 /etc/rc.shtdown 脚本,然后继续向所有进程发送 TERM 信号,随后向任何未及时终止的进程发送 KILL 信号。
要关闭支持电源管理的架构和系统上的FreeBSD机器,请立即使用 shutdown -p 关闭电源。要重新启动FreeBSD系统,请立即使用 shutdown -r 。必须有一名root或操作员才能运行 shutdown(8) 。还可以使用 halt(8) 和 reboot(8) 。有关更多信息,请参阅它们的手册页。
参照 【3.3. 用户和基本帐户管理】修改组成员资格。
xxxxxxxxxx电源管理要求将 acpi(4) 作为模块加载或静态编译到自定义内核中。