第 26 章。更新和升级 FreeBSD

26.1. 概述

FreeBSD 在版本发布之间不断开发。有些人更喜欢使用官方发布的版本,而有些人更喜欢与最新的开发保持同步。但是,即使是官方发布的版本,也经常更新以修复安全和其他关键问题。无论使用哪个版本,FreeBSD 都提供所有必要的工具来保持系统更新,并允许在版本之间轻松升级。本章描述了如何跟踪开发系统以及保持 FreeBSD 系统更新的基本工具。

阅读完本章后,您将了解

  • 如何使用 freebsd-update 或 Git 使 FreeBSD 系统保持最新。

  • 如何将已安装系统的状态与已知的原始副本进行比较。

  • 如何使用 Git 或文档端口使已安装的文档保持最新。

  • 两个开发分支之间的区别:FreeBSD-STABLE 和 FreeBSD-CURRENT。

  • 如何重建和重新安装整个基本系统。

在阅读本章之前,您应该

在本章中,git 用于获取和更新 FreeBSD 源代码。可选地,可以使用 devel/git 端口或包。

26.2. FreeBSD 更新

及时应用安全补丁并升级到操作系统的新版本是持续系统管理的重要方面。FreeBSD 包含一个名为 freebsd-update 的实用程序,可用于执行这两个任务。

此实用程序支持 FreeBSD 的二进制安全和勘误更新,无需手动编译和安装补丁或新内核。所有体系结构和安全团队当前支持的版本都提供二进制更新。支持的版本列表及其估计的生命周期结束日期列在 https://freebsd.ac.cn/security/ 上。

此实用程序还支持操作系统升级到次要版本,以及升级到其他发布分支。在升级到新版本之前,请查看其发布公告,因为其中包含与发布相关的重要的信息。发布公告可在 https://freebsd.ac.cn/releases/ 获取。

如果使用 crontab(5) 利用 freebsd-update(8) 的功能,则必须在升级操作系统之前禁用它。

本节介绍 freebsd-update 使用的配置文件,演示如何应用安全补丁以及如何升级到次要或主要操作系统版本,并讨论升级操作系统时的一些注意事项。

26.2.1. 配置文件

freebsd-update 的默认配置文件按原样使用。一些用户可能希望调整 /etc/freebsd-update.conf 中的默认配置,以便更好地控制该过程。此文件中的注释解释了可用的选项,但以下内容可能需要更多解释

# Components of the base system which should be kept updated.
Components world kernel

此参数控制 FreeBSD 的哪些部分将保持最新。默认值是更新整个基本系统和内核。相反,可以指定各个组件,例如 src/basesrc/sys。但是,最佳选择是保持默认值,因为将其更改为包含特定项目需要列出每个所需项目。随着时间的推移,这可能会造成灾难性的后果,因为源代码和二进制文件可能会不同步。

# Paths which start with anything matching an entry in an IgnorePaths
# statement will be ignored.
IgnorePaths /boot/kernel/linker.hints

要在更新过程中保留指定的目录,例如 /bin/sbin,请将其路径添加到此语句中。此选项可用于防止 freebsd-update 覆盖本地修改。

# Paths which start with anything matching an entry in an UpdateIfUnmodified
# statement will only be updated if the contents of the file have not been
# modified by the user (unless changes are merged; see below).
UpdateIfUnmodified /etc/ /var/ /root/ /.cshrc /.profile

此选项仅更新指定目录中未修改的配置文件。用户进行的任何更改都会阻止自动更新这些文件。还有一个选项 KeepModifiedMetadata,它将指示 freebsd-update 在合并期间保存更改。

# When upgrading to a new FreeBSD release, files which match MergeChanges
# will have any local changes merged into the version from the new release.
MergeChanges /etc/ /var/named/etc/ /boot/device.hints

包含 freebsd-update 应尝试合并的配置文件的目录列表。文件合并过程是一系列 diff(1) 补丁。合并要么被接受,要么打开编辑器,要么导致 freebsd-update 终止。如有疑问,请备份 /etc 并接受合并。

# Directory in which to store downloaded updates and temporary
# files used by FreeBSD Update.
# WorkDir /var/db/freebsd-update

此目录是放置所有补丁和临时文件的位置。在用户执行版本升级的情况下,此位置至少应该有 1 GB 的磁盘空间可用。

# When upgrading between releases, should the list of Components be
# read strictly (StrictComponents yes) or merely as a list of components
# which *might* be installed of which FreeBSD Update should figure out
# which actually are installed and upgrade those (StrictComponents no)?
# StrictComponents no

当此选项设置为 yes 时,freebsd-update 将假定 Components 列表完整,并且不会尝试对列表之外进行更改。实际上,freebsd-update 将尝试更新属于 Components 列表的每个文件。

有关更多详细信息,请参阅 freebsd-update.conf(5)

26.2.2. 应用安全补丁

应用 FreeBSD 安全补丁的过程已简化,允许管理员使用 freebsd-update 使系统保持完全修补。有关 FreeBSD 安全公告的更多信息,请参见 FreeBSD 安全公告

可以使用以下命令下载和安装 FreeBSD 安全补丁。第一个命令将确定是否有任何未决的补丁可用,如果有,将列出应用补丁后将修改的文件。第二个命令将应用补丁。

# freebsd-update fetch
# freebsd-update install

如果更新应用了任何内核补丁,则系统需要重新启动才能引导到已修补的内核。如果补丁已应用到任何正在运行的二进制文件,则应重新启动受影响的应用程序,以便使用已修补版本的二进制文件。

通常,用户需要准备重新启动系统。要了解系统是否需要由于内核更新而重新启动,请执行命令 freebsd-version -kuname -r。如果输出不同,请重新启动系统。

可以通过将以下条目添加到 /etc/crontab 来配置系统每天自动检查更新

@daily                                  root    freebsd-update cron

如果存在补丁,它们将自动下载,但不会应用。将向 root 用户发送电子邮件,以便可以查看补丁并使用 freebsd-update install 手动安装。

如果出现任何问题,freebsd-update 可以使用以下命令回滚最后一组更改

# freebsd-update rollback
Uninstalling updates... done.

同样,如果内核或任何内核模块已被修改,则应重新启动系统,并且应重新启动任何受影响的二进制文件。

只有 GENERIC 内核可以由 freebsd-update 自动更新。如果安装了自定义内核,则必须在 freebsd-update 完成安装更新后重建和重新安装它。默认内核名称为 GENERIC。可以使用 uname(1) 命令来验证其安装。

始终在 /boot/GENERIC 中保留 GENERIC 内核的副本。这将有助于诊断各种问题并执行版本升级。有关如何获取 GENERIC 内核副本的说明,请参阅 FreeBSD 9.X 及更高版本中的自定义内核

除非 /etc/freebsd-update.conf 中的默认配置已更改,否则 freebsd-update 将与其他更新一起安装更新的内核源代码。然后,可以按照通常的方式重建和重新安装新的自定义内核。

freebsd-update 分发的更新并不总是涉及内核。如果 freebsd-update install 未修改内核源代码,则无需重建自定义内核。但是,freebsd-update 将始终更新 /usr/src/sys/conf/newvers.sh。当前补丁级别(由 uname -r 报告的 -p 编号表示)是从此文件获取的。即使没有其他更改,重建自定义内核也允许 uname 准确报告系统的当前补丁级别。这在维护多个系统时特别有用,因为它允许快速评估每个系统中安装的更新。

26.2.3. 执行次要和主要版本升级

从一个 FreeBSD 次要版本升级到另一个版本称为次要版本升级。例如

  • FreeBSD 13.1 到 13.2。

主要版本升级会增加主要版本号。例如

  • FreeBSD 13.2 到 14.0。

两种类型的升级都可以通过向 freebsd-update 提供发布版本目标来执行。

在每个新的 RELEASE 之后,FreeBSD 包构建服务器将在有限的时间内使用操作系统的新版本。这为许多不立即在发布公告后升级的用户提供了连续性。例如

  • 针对 13.1 和 13.2 用户的软件包将构建在运行 13.1 的服务器上,直到 13.1 达到生命周期结束

 — 并且,至关重要的是

  • 在 13.1 上构建的内核模块可能适合 13.2。

因此,对于任何次要或主要的操作系统升级,如果您的软件包需求包括任何内核模块

  • 准备好从源代码构建模块.

如果系统正在运行自定义内核,请确保在开始升级之前,GENERIC 内核的副本存在于 /boot/GENERIC 中。有关如何获取 GENERIC 内核副本的说明,请参阅 FreeBSD 9.X 及更高版本中的自定义内核

在升级到新版本之前,请确保现有的 FreeBSD 安装相对于安全和勘误补丁是最新的

# freebsd-update fetch
# freebsd-update install

以下命令在 FreeBSD 13.1 系统上运行时,将将其升级到 FreeBSD 13.2

# freebsd-update -r 13.2-RELEASE upgrade

收到命令后,freebsd-update 将评估配置文件和当前系统,以尝试收集执行升级所需的信息。屏幕列表将显示哪些组件已检测到和未检测到。例如

Looking up update.FreeBSD.org mirrors... 1 mirrors found.
Fetching metadata signature for 13.1-RELEASE from update1.FreeBSD.org... done.
Fetching metadata index... done.
Inspecting system... done.

The following components of FreeBSD seem to be installed:
kernel/smp src/base src/bin src/contrib src/crypto src/etc src/games
src/gnu src/include src/krb5 src/lib src/libexec src/release src/rescue
src/sbin src/secure src/share src/sys src/tools src/ubin src/usbin
world/base world/info world/lib32 world/manpages

The following components of FreeBSD do not seem to be installed:
kernel/generic world/catpages world/dict world/doc world/games
world/proflibs

Does this look reasonable (y/n)? y

此时,freebsd-update 将尝试下载升级所需的所有文件。在某些情况下,系统可能会提示用户有关要安装的内容或如何继续的提示。

使用自定义内核时,上述步骤将产生类似于以下内容的警告

WARNING: This system is running a "MYKERNEL" kernel, which is not a
kernel configuration distributed as part of FreeBSD 13.1-RELEASE.
This kernel will not be updated: you MUST update the kernel manually
before running "/usr/sbin/freebsd-update install"

此时可以安全地忽略此警告。更新的 GENERIC 内核将用作升级过程中的中间步骤。

一旦所有补丁下载到本地系统,它们将被应用。此过程可能需要一段时间,具体取决于机器的速度和工作负载。然后将合并配置文件。合并过程需要一些用户干预,因为文件可能被合并或屏幕上可能会出现一个编辑器以进行手动合并。随着流程的继续,每个成功合并的结果都会显示给用户。合并失败或忽略将导致流程中止。用户可能希望备份 /etc 并稍后手动合并重要文件,例如 master.passwdgroup

系统尚未被修改,因为所有打补丁和合并都发生在另一个目录中。一旦所有补丁成功应用,所有配置文件都已合并,并且该过程似乎将顺利进行,用户可以使用以下命令将更改提交到磁盘。

# freebsd-update install

内核和内核模块将首先被修补。如果系统正在运行自定义内核,请使用 nextboot(8) 将下次启动的内核设置为更新后的 /boot/GENERIC

# nextboot -k GENERIC

在使用 GENERIC 内核重启之前,请确保它包含系统正常启动和连接到网络所需的所有驱动程序(如果正在更新的机器是远程访问的)。特别是,如果正在运行的自定义内核包含通常由内核模块提供的内置功能,请确保使用 /boot/loader.conf 功能将这些模块临时加载到 GENERIC 内核中。建议在升级过程完成之前,禁用非必要服务以及任何磁盘和网络挂载。

现在应该使用更新的内核重新启动机器。

# shutdown -r now

一旦系统恢复在线,请使用以下命令重新启动 freebsd-update。由于该过程的状态已保存,freebsd-update 将不会从头开始,而是会继续进行到下一阶段并删除所有旧的共享库和目标文件。

# freebsd-update install

根据是否有任何库版本号被提升,可能只有两个安装阶段而不是三个。

升级现在已完成。如果这是主要版本升级,请重新安装所有端口和软件包,如 主要版本升级后升级软件包 中所述。

26.2.3.1. FreeBSD 9.X 及更高版本的自定义内核

在使用 freebsd-update 之前,请确保 GENERIC 内核的副本存在于 /boot/GENERIC 中。如果自定义内核只构建过一次,则 /boot/kernel.old 中的内核是 GENERIC 内核。只需将此目录重命名为 /boot/GENERIC

如果自定义内核已构建多次,或者不知道自定义内核构建了多少次,请获取与当前操作系统版本匹配的 GENERIC 内核的副本。如果可以物理访问系统,则可以从安装介质安装 GENERIC 内核的副本。

# mount /cdrom
# cd /cdrom/usr/freebsd-dist
# tar -C/ -xvf kernel.txz boot/kernel/kernel

或者,可以从源代码重新构建并安装 GENERIC 内核。

# cd /usr/src
# make kernel __MAKE_CONF=/dev/null SRCCONF=/dev/null

为了让 freebsd-update 将此内核识别为 GENERIC 内核,GENERIC 配置文件不得以任何方式修改。还建议在没有其他特殊选项的情况下构建内核。

不需要重新启动到 GENERIC 内核,因为 freebsd-update 只需要 /boot/GENERIC 存在。

26.2.3.2. 主要版本升级后升级软件包

通常,安装的应用程序在次要版本升级后将继续正常工作。主要版本使用不同的应用程序二进制接口 (ABI),这将破坏大多数第三方应用程序。在主要版本升级后,需要升级所有安装的软件包和端口。可以使用 pkg upgrade 升级软件包。要升级安装的端口,请使用 ports-mgmt/portmaster 等工具。

对所有安装的软件包进行强制升级将用存储库中的最新版本替换软件包,即使版本号没有增加。这是必需的,因为在 FreeBSD 主要版本之间升级时,ABI 版本发生了变化。可以通过执行以下操作来完成强制升级。

# pkg-static upgrade -f

可以使用此命令重建所有安装的应用程序。

# portmaster -af

此命令将显示每个具有可配置选项的应用程序的配置屏幕,并等待用户与这些屏幕交互。要防止此行为,并仅使用默认选项,请在上述命令中包含 -G

软件升级完成后,使用对 freebsd-update 的最终调用完成升级过程,以便在升级过程中结束所有松散的环节。

# freebsd-update install

如果 GENERIC 内核被临时使用,现在是使用 配置 FreeBSD 内核 中的说明构建和安装新的自定义内核的时候了。

重新启动机器到新的 FreeBSD 版本。升级过程现在已完成。

26.2.4. 系统状态比较

可以使用 freebsd-update IDS 测试已安装的 FreeBSD 版本与已知良好副本的状态。此命令评估系统实用程序、库和配置文件的当前版本,可以用作内置入侵检测系统 (IDS)。

此命令不能替代真正的 IDS,例如 security/snort。由于 freebsd-update 在磁盘上存储数据,因此存在篡改的可能性。虽然可以使用 kern.securelevel 减少这种可能性,并且在不使用时将 freebsd-update 数据存储在只读文件系统中,但更好的解决方案是将系统与安全磁盘(如 DVD 或安全存储的外部 USB 磁盘设备)进行比较。 二进制验证 中描述了使用内置实用程序提供 IDS 功能的另一种方法。

要开始比较,请指定要保存结果的输出文件。

# freebsd-update IDS >> outfile.ids

系统现在将被检查,并将文件以及版本中已知值和当前安装的 SHA256 哈希值的冗长列表发送到指定的输出文件。

列表中的条目非常长,但输出格式可以轻松解析。例如,要获取与版本中不同的所有文件的列表,请发出以下命令。

# cat outfile.ids | awk '{ print $1 }' | more
/etc/master.passwd
/etc/motd
/etc/passwd
/etc/pf.conf

此示例输出已被截断,因为存在更多文件。一些文件有自然的修改。例如,如果用户已添加到系统,则 /etc/passwd 将被修改。内核模块可能不同,因为 freebsd-update 可能已更新它们。要排除特定文件或目录,请将它们添加到 /etc/freebsd-update.conf 中的 IDSIgnorePaths 选项中。

26.3. 更新引导代码

以下手册描述了引导代码和引导加载程序的升级过程: gpart(8)gptboot(8)gptzfsboot(8)loader.efi(8)

26.4. 更新文档集

文档是 FreeBSD 操作系统不可或缺的一部分。虽然 FreeBSD 网站上始终提供最新的 FreeBSD 文档版本 (Documentation Portal),但拥有 FreeBSD 网站、手册、常见问题解答和文章的最新本地副本非常方便。

本节介绍如何使用源代码或 FreeBSD 端口集合来保持 FreeBSD 文档的本地副本最新。

有关编辑和提交文档更正的信息,请参阅面向新贡献者的 FreeBSD 文档项目入门 (面向新贡献者的 FreeBSD 文档项目入门)。

26.4.1. 从源代码更新文档

从源代码重新构建 FreeBSD 文档需要一个工具集合,这些工具不是 FreeBSD 基本系统的一部分。可以使用 这些步骤 从 FreeBSD 文档项目入门中安装所需的工具。

安装完成后,使用 git 获取文档源代码的干净副本。

# git clone https://git.FreeBSD.org/doc.git /usr/doc

文档源代码的初始下载可能需要一段时间。让它运行直到完成。

将来可以运行以下命令来获取文档源代码的更新。

# git pull

一旦将文档源代码的最新快照获取到 /usr/doc,一切就已准备好更新安装的文档。

可以通过键入以下命令来执行完全更新。

# cd /usr/doc
# make

26.5. 跟踪开发分支

FreeBSD 有两个开发分支:FreeBSD-CURRENT 和 FreeBSD-STABLE。

本节解释每个分支及其目标受众,以及如何使系统与每个相应分支保持最新。

26.5.1. 使用 FreeBSD-CURRENT

FreeBSD-CURRENT 是 FreeBSD 开发的“前沿”,预计 FreeBSD-CURRENT 用户将具有很高的技术技能。希望跟踪开发分支的非技术用户应该改用 FreeBSD-STABLE。

FreeBSD-CURRENT 是 FreeBSD 的最新源代码,包括正在进行的工作、实验性更改和可能存在也可能不存在于下一个官方版本中的过渡机制。虽然许多 FreeBSD 开发人员每天都编译 FreeBSD-CURRENT 源代码,但在某些时间段内,源代码可能无法构建。这些问题将尽快解决,但 FreeBSD-CURRENT 是带来灾难还是带来新功能可能取决于源代码同步的时间。

FreeBSD-CURRENT 是为三个主要利益集团提供的。

  1. 积极参与源代码树某部分的 FreeBSD 社区成员。

  2. 积极测试的 FreeBSD 社区成员。他们愿意花时间解决问题,就 FreeBSD 的更改和总体方向提出主题建议,并提交补丁。

  3. 希望关注事态发展、将当前源代码用于参考目的或偶尔发表评论或代码贡献的用户。

FreeBSD-CURRENT 不应被视为在下一个版本发布之前获得新功能的快速通道,因为预发布功能尚未经过充分测试,很可能包含错误。它不是获取错误修复的快速方法,因为任何给定的提交都有可能引入新错误,就像修复现有错误一样。FreeBSD-CURRENT 没有任何“官方支持”。

要跟踪 FreeBSD-CURRENT。

  1. 加入 FreeBSD-CURRENT 邮件列表src 存储库主分支的提交消息 列表。这对于查看人们对系统当前状态的评论以及接收有关 FreeBSD-CURRENT 当前状态的重要公告来说是必不可少的

    src 存储库主分支的提交消息 列表记录每次更改的提交日志条目,以及任何关于可能副作用的相关信息。

    要加入这些列表,请访问 FreeBSD 列表服务器,单击要订阅的列表,然后按照说明进行操作。为了跟踪对整个源代码树的更改,而不仅仅是 FreeBSD-CURRENT 的更改,请订阅 src 存储库所有分支的提交消息

  2. 与 FreeBSD-CURRENT 源代码同步。通常,使用 git 从 FreeBSD Git 仓库的 main 分支检出 -CURRENT 代码(有关详细信息,请参阅 “使用 Git”)。

  3. 由于仓库的大小,一些用户选择仅同步他们感兴趣的或正在为其贡献补丁的源代码部分。但是,计划从源代码编译操作系统的用户必须下载所有 FreeBSD-CURRENT,而不仅仅是选定的部分。

    在编译 FreeBSD-CURRENT 之前,请仔细阅读 /usr/src/Makefile 并遵循 从源代码更新 FreeBSD 中的说明。阅读 FreeBSD-CURRENT 邮件列表/usr/src/UPDATING 以了解在通往下一个版本的道路上有时变得必要的其他引导程序。

  4. 积极参与!鼓励 FreeBSD-CURRENT 用户提交他们对增强功能或错误修复的建议。包含附带代码的建议总是受欢迎的。

26.5.2. 使用 FreeBSD-STABLE

FreeBSD-STABLE 是用于发布主要版本的开发分支。更改以较慢的速度进入此分支,并通常假设它们已首先在 FreeBSD-CURRENT 中进行了测试。这仍然是一个开发分支,在任何给定时间,FreeBSD-STABLE 的源代码可能适合或可能不适合一般使用。它只是一个工程开发轨道,而不是最终用户的资源。没有资源进行测试的用户应该改为运行 FreeBSD 的最新版本。

那些有兴趣跟踪或贡献 FreeBSD 开发过程的人,特别是与 FreeBSD 的下一个版本相关的过程,应该考虑关注 FreeBSD-STABLE。

虽然 FreeBSD-STABLE 分支应该始终编译并运行,但这不能保证。由于运行 FreeBSD-STABLE 的人比 FreeBSD-CURRENT 多,因此 FreeBSD-STABLE 中有时会出现 FreeBSD-CURRENT 中没有发现的错误和极端情况是不可避免的。出于这个原因,不应该盲目跟踪 FreeBSD-STABLE。特别重要的是不要在没有在开发或测试环境中彻底测试代码的情况下将任何生产服务器更新到 FreeBSD-STABLE。

跟踪 FreeBSD-STABLE

  1. 加入 FreeBSD-STABLE 邮件列表 以了解可能出现在 FreeBSD-STABLE 中的构建依赖项或需要特别注意的任何其他问题。开发人员还将在他们考虑一些有争议的修复或更新时在此邮件列表中发布公告,让用户有机会在他们有任何问题时提出他们对拟议更改的任何问题。

    加入正在跟踪的分支的相关 git 列表。例如,跟踪 14-STABLE 分支的用户应该加入 src 存储库稳定分支的提交消息。此列表记录了每次更改的提交日志条目,以及有关可能副作用的相关信息。

    要加入这些列表,请访问 FreeBSD 列表服务器,点击要订阅的列表,然后按照说明操作。要跟踪整个源代码树的更改,请订阅 src 存储库所有分支的提交消息

  2. 要安装新的 FreeBSD-STABLE 系统,请从 FreeBSD 镜像站点 安装最新的 FreeBSD-STABLE 版本,或使用从 FreeBSD-STABLE 构建的每月快照。有关快照的更多信息,请参阅 www.freebsd.org/snapshots

    要编译或将现有 FreeBSD 系统升级到 FreeBSD-STABLE,请使用 git 检出所需分支的源代码。分支名称,例如 stable/13,列在 www.freebsd.org/releng 中。

  3. 在编译或升级到 FreeBSD-STABLE 之前,请仔细阅读 /usr/src/Makefile 并遵循 从源代码更新 FreeBSD 中的说明。阅读 FreeBSD-STABLE 邮件列表/usr/src/UPDATING 以了解在通往下一个版本的道路上有时变得必要的其他引导程序。

26.5.3. N 号

在跟踪错误时,了解用于创建出现问题的系统的源代码版本非常重要。FreeBSD 提供了编译到内核中的版本信息。 uname(1) 检索此信息,例如

% uname -v
FreeBSD 14.0-CURRENT #112 main-n247514-031260d64c18: Tue Jun 22 20:43:19 MDT 2021     fred@machine:/usr/home/fred/obj/usr/home/fred/git/head/amd64.amd64/sys/FRED

最后一个字段提供有关内核名称、构建内核的人员以及编译内核的位置的信息。查看第 4 个字段,它由几个部分组成

main-n247514-031260d64c18

main		(1)
n247514		(2)
031260d64c18	(3)
		(4)
1Git 分支名称。注意:n 号的比较仅对项目发布的分支(mainstable/XXreleng/XX)有效。本地分支将具有与父分支的提交重叠的 n 号。
2n 号是从 Git 存储库开始到 Git 哈希的线性提交计数,从行中包含的 Git 哈希开始。
3已检出树的 Git 哈希
4有时当内核是在具有未提交更改的树中构建时,会存在 -dirty 后缀。在此示例中它不存在,因为 FRED 内核是从原始检出中构建的。

git rev-list 命令用于查找与 Git 哈希相对应的 n 号。例如

% git rev-list --first-parent --count 031260d64c18 (1)
247514 (2)
1要转换的 Git 哈希(使用上面示例中的哈希)
2n 号。

通常这个数字并不重要。但是,当修复错误被提交时,这个数字可以很容易地快速确定修复是否存在于当前运行的系统中。开发人员通常会引用提交的哈希(或提供包含该哈希的 URL),而不是 n 号,因为哈希是更改的易于查看的标识符,而 n 号则不是。安全建议和勘误通知也将记录 n 号,可以将其直接与您的系统进行比较。当您需要使用浅 Git 克隆时,您无法可靠地比较 n 号,因为 git rev-list 命令会计算仓库中所有浅克隆省略的修订版。

26.6. 从源代码更新 FreeBSD

通过从源代码编译来更新 FreeBSD 比二进制更新提供了几个优势。代码可以使用选项构建以利用特定硬件。基础系统的部分可以构建为非默认设置,或者在不需要或不希望的情况下完全排除。与仅安装二进制更新相比,构建过程需要更长的时间来更新系统,但允许完全自定义以生成 FreeBSD 的定制版本。

26.6.1. 快速入门

这是用于通过从源代码构建来更新 FreeBSD 的典型步骤的快速参考。后面的部分将更详细地描述该过程。

mergemaster(8) 切换到 etcupdate(8) 时,第一次运行可能会错误地合并更改,从而产生虚假的冲突。为了防止这种情况,请在更新源代码和构建新世界之前执行以下步骤

# etcupdate extract (1)
# etcupdate diff (2)
1引导库存 /etc 文件的数据库;有关更多信息,请参阅 etcupdate(8)
2检查引导后的差异。修剪不再需要的任何本地更改,以减少将来更新中出现冲突的可能性。
  • 更新和构建

    # git pull -C /usr/src  (1)
    check /usr/src/UPDATING  (2)
    # cd /usr/src          (3)
    # make -j4 buildworld  (4)
    # make -j4 kernel      (5)
    # shutdown -r now      (6)
    # etcupdate -p         (7)
    # cd /usr/src          (8)
    # make installworld    (9)
    # etcupdate -B         (10)
    # shutdown -r now      (11)
1获取最新版本的源代码。有关获取和更新源代码的更多信息,请参阅 更新源代码
2检查 /usr/src/UPDATING 以了解从源代码构建之前或之后需要执行的任何手动步骤。
3转到源代码目录。
4编译世界,除了内核之外的所有内容。
5编译并安装内核。这等效于 make buildkernel installkernel
6将系统重新引导到新内核。
7更新和合并 /etc/ 中在 installworld 之前需要的配置文件。
8转到源代码目录。
9安装世界。
10更新和合并 /etc/ 中的配置文件。
11重新启动系统以使用新构建的世界和内核。

26.6.2. 准备源代码更新

阅读 /usr/src/UPDATING。此文件中描述了在更新之前或之后必须执行的任何手动步骤。

26.6.3. 更新源代码

FreeBSD 源代码位于 /usr/src/ 中。更新此源代码的首选方法是通过 Git 版本控制系统。验证源代码是否处于版本控制下

# cd /usr/src
# git remote --v
origin  https://git.freebsd.org/src.git (fetch)
origin  https://git.freebsd.org/src.git (push)

这表明 /usr/src/ 处于版本控制之下,可以使用 git(1) 更新

# git pull -C /usr/src

如果目录最近没有更新,更新过程可能需要一些时间。完成后,源代码将是最新的,并且下一节中描述的构建过程可以开始。

获取源代码

如果输出显示 fatal: not a git repository,则那里的文件丢失或使用其他方法安装。需要重新检出源代码。

表 1. FreeBSD 版本和仓库分支
uname ‑r 输出仓库路径描述

X.Y-RELEASE

releng/X.Y

发布版本,再加上只有关键的安全和错误修复补丁。此分支推荐给大多数用户。

X.Y-STABLE

stable/X

发布版本,再加上该分支的所有其他开发。STABLE 指的是应用程序二进制接口 (ABI) 不变,因此为早期版本编译的软件仍然可以运行。例如,编译为在 FreeBSD 10.1 上运行的软件仍将在稍后编译的 FreeBSD 10-STABLE 上运行。

STABLE 分支偶尔会有一些错误或不兼容性,可能会影响用户,但这些问题通常会很快得到解决。

X-CURRENT

main

FreeBSD 的最新未发布开发版本。CURRENT 分支可能存在重大错误或不兼容性,仅推荐给高级用户。

使用 uname(1) 确定正在使用的 FreeBSD 版本

# uname -r
13.2-RELEASE

根据 FreeBSD 版本和仓库分支,用于更新 13.2-RELEASE 的源代码的仓库路径为 releng/13.2。此路径在检出源代码时使用

# mv /usr/src /usr/src.bak (1)
# git clone --branch releng/13.2 https://git.FreeBSD.org/src.git /usr/src (2)
1将旧目录移出。如果此目录中没有本地修改,则可以将其删除。
2FreeBSD 版本和仓库分支 中的路径添加到仓库 URL。第三个参数是本地系统上源代码的目标目录。

26.6.4. 从源代码构建

世界,或操作系统中除了内核以外的所有内容,都是编译的。这首先是为了提供最新的工具来构建内核。然后构建内核本身。

# cd /usr/src
# make buildworld
# make buildkernel

编译后的代码被写入到 /usr/obj

这些是基本步骤。控制构建的其他选项将在下面介绍。

26.6.4.1. 执行干净构建

某些版本的 FreeBSD 构建系统会在临时对象目录 /usr/obj 中保留之前编译的代码。这可以通过避免重新编译未更改的代码来加快以后的构建。要强制对所有内容进行干净重建,请在开始构建之前使用 cleanworld

# make cleanworld

26.6.4.2. 设置作业数量

在多核处理器上增加构建作业的数量可以提高构建速度。使用 sysctl hw.ncpu 确定内核数量。处理器各不相同,与不同版本的 FreeBSD 一起使用的构建系统也是如此,因此测试是确定不同数量的作业如何影响构建速度的唯一可靠方法。作为起点,请考虑内核数量的一半到两倍之间的值。作业数量由 -j 指定。

示例 1. 增加构建作业数量

使用四个作业构建世界和内核

# make -j4 buildworld buildkernel

26.6.4.3. 只构建内核

如果源代码已更改,则必须完成 buildworld。之后,可以随时运行 buildkernel 来构建内核。要只构建内核

# cd /usr/src
# make buildkernel

26.6.4.4. 构建自定义内核

标准 FreeBSD 内核基于一个名为 GENERIC内核配置文件GENERIC 内核包含最常用的设备驱动程序和选项。有时,构建自定义内核、添加或删除设备驱动程序或选项以满足特定需求是有用或必要的。

例如,开发小型嵌入式计算机的人员,其 RAM 非常有限,可以删除不必要的设备驱动程序或选项,以使内核略微变小。

内核配置文件位于 /usr/src/sys/arch/conf/ 中,其中 archuname -m 的输出。在大多数计算机上,它是 amd64,从而得到一个配置文件目录 /usr/src/sys/amd64/conf/

/usr/src 可以删除或重新创建,因此最好将自定义内核配置文件保存在单独的目录中,例如 /root。将内核配置文件链接到 conf 目录中。如果该目录被删除或覆盖,则可以将内核配置文件重新链接到新的目录中。

可以通过复制 GENERIC 配置文件来创建自定义配置文件。在本例中,新的自定义内核是为存储服务器而创建的,因此命名为 STORAGESERVER

# cp /usr/src/sys/amd64/conf/GENERIC /root/STORAGESERVER
# cd /usr/src/sys/amd64/conf
# ln -s /root/STORAGESERVER .

然后编辑 /root/STORAGESERVER,添加或删除设备或选项,如 config(5) 中所示。

通过在命令行上将 KERNCONF 设置为内核配置文件来构建自定义内核

# make buildkernel KERNCONF=STORAGESERVER

26.6.5. 安装编译后的代码

完成 buildworldbuildkernel 步骤后,将安装新的内核和世界

# cd /usr/src
# make installkernel
# shutdown -r now
# cd /usr/src
# make installworld
# shutdown -r now

如果构建了自定义内核,则还必须设置 KERNCONF 以使用新的自定义内核

# cd /usr/src
# make installkernel KERNCONF=STORAGESERVER
# shutdown -r now
# cd /usr/src
# make installworld
# shutdown -r now

26.6.6. 完成更新

一些最终的任务将完成更新。任何修改的配置文件都将与新版本合并,过时的库将被定位并删除,然后系统将重新启动。

26.6.6.1. 使用 etcupdate(8) 合并配置文件

etcupdate(8) 是一个用于管理对在 installworld 中未更新的文件的更新的工具,例如位于 /etc/ 中的文件。它通过对这些文件所做的更改与本地版本进行三方合并来管理更新。 etcupdate(8) 旨在最大限度地减少用户干预。

通常,etcupdate(8) 不需要任何特定参数来执行其任务。但是,有一个方便的中间命令,用于在第一次使用 etcupdate(8) 时进行完整性检查

# etcupdate diff

此命令允许用户审核配置更改。

如果 etcupdate(8) 无法自动合并文件,则可以通过手动交互来解决合并冲突,方法是发出以下命令

# etcupdate resolve

mergemaster(8) 切换到 etcupdate(8) 时,第一次运行可能会错误地合并更改,从而产生虚假的冲突。为了防止这种情况,请在更新源代码和构建新世界之前执行以下步骤

# etcupdate extract (1)
# etcupdate diff (2)
1引导库存 /etc 文件的数据库;有关更多信息,请参阅 etcupdate(8)
2检查引导后的差异。修剪不再需要的任何本地更改,以减少将来更新中出现冲突的可能性。

26.6.6.2. 检查过时文件和库

更新后,可能会保留一些过时文件或目录。这些文件可以被定位

# make check-old

并删除

# make delete-old

也可能保留一些过时的库。这些可以通过以下方式检测到

# make check-old-libs

并通过以下方式删除

# make delete-old-libs

在删除库后,仍然使用这些旧库的程序将停止工作。这些程序必须在删除旧库后重新构建或替换。

当所有旧文件或目录都被认为可以安全删除时,可以通过在命令中设置 BATCH_DELETE_OLD_FILES 来避免按 yEnter 删除每个文件。例如

# make BATCH_DELETE_OLD_FILES=yes delete-old-libs

26.6.6.3. 更新后重启

更新后的最后一步是重启计算机,使所有更改生效

# shutdown -r now

26.7. 多台机器的跟踪

当多台机器需要跟踪相同的源树时,让每台系统都下载源代码并重新构建所有内容是一种浪费磁盘空间、网络带宽和 CPU 周期的做法。解决方法是让一台机器完成大部分工作,而其余机器通过 NFS 挂载该工作。本节概述了一种执行此操作的方法。有关使用 NFS 的更多信息,请参阅 网络文件系统 (NFS)

首先,确定一组将运行相同二进制文件集的机器,称为构建集。每台机器都可以有自定义内核,但将运行相同用户空间二进制文件。从该集合中,选择一台机器作为构建机器,在该机器上构建世界和内核。理想情况下,这是一台速度快的机器,有足够的备用 CPU 来运行 make buildworldmake buildkernel

选择一台机器作为测试机器,该机器将在软件更新投入生产之前对其进行测试。这必须是一台能够承受长时间停机的机器。它可以是构建机器,但不必是。

该构建集中的所有机器都需要通过 NFS 从构建机器挂载 /usr/obj/usr/src。对于多个构建集,/usr/src 应该位于一台构建机器上,并通过 NFS 挂载到其他机器上。

确保构建集中的所有机器上的 /etc/make.conf/etc/src.conf 与构建机器一致。这意味着构建机器必须构建构建集中的任何机器都将安装的基础系统的全部部分。此外,每台构建机器都应该在 /etc/make.conf 中使用 KERNCONF 设置其内核名称,并且构建机器应该将其全部列在其 KERNCONF 中,首先列出其自身的内核。构建机器必须在其 /usr/src/sys/arch/conf 中拥有每台机器的内核配置文件。

在构建机器上,按照 从源代码更新 FreeBSD 中的说明构建内核和世界,但不要在构建机器上安装任何内容。相反,将构建的内核安装到测试机器上。在测试机器上,通过 NFS 挂载 /usr/src/usr/obj。然后,运行 shutdown now 以进入单用户模式,以便安装新的内核和世界并照常运行 etcupdate。完成后,重新引导以返回到正常的多个用户操作。

在验证测试机器上的所有内容都正常工作后,使用相同的步骤将新软件安装到构建集中其余的每台机器上。

相同的机制可用于端口树。第一步是通过 NFS 共享 /usr/ports 给构建集中的所有机器。要配置 /etc/make.conf 以共享 distfiles,请将 DISTDIR 设置为一个公共共享目录,该目录可供 NFS 挂载映射到的用户 root 写入。每台机器都应该将 WRKDIRPREFIX 设置为本地构建目录,如果要在本地构建端口。或者,如果构建系统要构建并向构建集中的机器分发软件包,则将构建系统上的 PACKAGES 设置为类似于 DISTDIR 的目录。

26.8. 在非 FreeBSD 主机上构建

历史上,构建 FreeBSD 需要一台 FreeBSD 主机。如今,FreeBSD 可以在 Linux 发行版和 macOS 上构建。

要在非 FreeBSD 主机上构建 FreeBSD,建议使用 tools/build/make.py 脚本。此脚本充当 bmake 的包装器,bmake 是 FreeBSD 使用的 make 实现。它确保必要的工具(包括实际的 FreeBSD 的 make(1))已引导并构建环境已正确配置。特别是,它设置了外部工具链变量,例如 XCCXLD 等。此外,该脚本可以将任何其他命令参数(例如 -j 4 用于并行构建或特定 make 目标)传递给 bmake

也可以使用最新版本的 bmake 来代替 tools/build/make.py 脚本。但是,在这种情况下,需要手动设置所需的環境变量(获取这些变量列表的最简单方法是运行 tools/build/make.py --debug)。

否则,构建 FreeBSD 的先决条件列表相当短。实际上,它归结为安装几个依赖项。

在 macOS 上,唯一依赖项是 LLVM。可以使用软件包管理器(例如,Homebrew)安装必要的依赖项。

brew install llvm

在 Linux 发行版上,安装 Clang 10.0 或更高版本,以及 libarchive 和 libbz2 的标头(通常打包为 libarchive-dev 和 libbz2-dev)。

安装完依赖项后,主机应该能够构建 FreeBSD。

例如,以下 tools/build/make.py 调用会构建世界

MAKEOBJDIRPREFIX=/tmp/obj tools/build/make.py -j 8 TARGET=arm64 TARGET_ARCH=aarch64 buildworld

它为目标 aarch64:arm64 在 8 个 CPU 上构建世界,并使用 /tmp/obj 来存储目标文件。请注意,在非 FreeBSD 主机上构建时,变量 MAKEOBJDIRPREFIXTARGETTARGET_ARCH 是必需的。此外,请确保创建由 MAKEOBJDIRPREFIX 环境变量指向的目标目录。

有关更多详细信息,请参阅 arch(7)build(7)


最后修改于:2024 年 9 月 23 日,作者 Fernando Apesteguía