26.6.从源代码更新 FreeBSD

通过从源代码编译更新 FreeBSD 相比于使用二进制更新,提供了几个优势。代码可以根据特定硬件的需求进行编译。部分基本系统可以使用非默认设置进行构建,或者在不需要或不想要的情况下完全省略。尽管构建过程比直接安装二进制更新花费更长的时间,但它允许完全定制,生成一个量身定制的 FreeBSD 版本。

26.6.1. 快速开始

这是通过从源代码编译更新 FreeBSD 的典型步骤的快速参考。后续章节将详细描述该过程。

警告

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

# etcupdate extract ①
# etcupdate diff    ②
  • ① 引导 /etc 文件的数据库;有关更多信息,请参见 etcupdate(8)

  • ② 引导数据库后检查差异。修剪任何不再需要的本地更改,以减少未来更新时发生冲突的可能性。

  • 更新和构建

    # git -C /usr/src pull   ①
    检查 /usr/src/UPDATING   
    # cd /usr/src            ③
    # make -j4 buildworld    ④
    # make -j4 kernel        ⑤
    # shutdown -r now        ⑥
    # etcupdate -p           ⑦
    # cd /usr/src            ⑧
    # make installworld      ⑨
    # etcupdate -B           ⑩
    # shutdown -r now        ⑪
  • ① 获取最新的源代码版本。有关获取和更新源代码的更多信息,请参见 更新源代码

  • ② 检查 /usr/src/UPDATING 文件,了解构建源代码之前或之后是否需要手动执行的步骤。

  • ③ 进入源代码目录。

  • ④ 编译 world,即编译除内核以外的所有内容。

  • ⑤ 编译并安装内核。这相当于 make buildkernel installkernel

  • ⑥ 重启系统以使用新内核。

  • ⑦ 更新并合并 /etc/ 中的配置文件,准备进行 installworld 安装。

  • ⑧ 进入源代码目录。

  • ⑨ 安装 world。

  • ⑩ 更新并合并 /etc/ 中的配置文件。

  • ⑪ 重启系统以使用新构建的 world 和内核。

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 -C /usr/src pull

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

注意

获取源代码:

如果输出显示 fatal: not a git repository,说明文件丢失或使用了不同的方法安装。需要重新克隆源代码。

表 1. FreeBSD 版本和仓库分支

uname ‑r 输出
仓库路径
描述

X.Y-RELEASE

releng/X.Y

版本发布版,仅包含关键的安全和错误修复补丁。该分支推荐给大多数用户。

X.Y-STABLE

stable/X

版本发布版,包含该分支的所有开发更新。STABLE 指的是应用二进制接口(ABI)不发生变化,因此早期版本编译的软件仍然可以在 FreeBSD 10-STABLE 上运行。例如,编译并运行在 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 ①
# git clone --branch releng/13.2 https://git.FreeBSD.org/src.git /usr/src ②
  • ① 将旧目录移到其他位置。如果该目录中没有本地修改,可以删除它。

  • FreeBSD 版本和仓库分支 中的路径已添加到仓库 URL 中。第三个参数是源代码在本地系统上的目标目录。

26.6.4. 从源代码构建

world(即操作系统的所有部分,除了内核)需要先进行编译,以提供更新的工具来构建内核。然后,内核本身会被编译:

# 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. 增加构建任务数量

使用四个任务来构建 world 和内核:

# 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 命令的输出。在大多数计算机上,archamd64,因此配置文件目录为 /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 步骤后,安装新的内核和 world:

# 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 ①
# etcupdate diff    ②
  • ① 引导 /etc 文件的数据库;更多信息,请参见 etcupdate(8)

  • ② 在引导后检查 diff。修剪任何不再需要的本地更改,以减少未来更新时的冲突可能性。

26.6.6.2. 检查过时的文件和库

更新后,可能会有一些过时的文件或目录残留。这些文件可以通过以下命令定位:

# make check-old

并删除:

# make delete-old

一些过时的库也可能残留,可以通过以下命令检测:

# make check-old-libs

并通过以下命令删除:

# make delete-old-libs

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

技巧

当确认所有旧文件或目录可以安全删除时,通过设置 BATCH_DELETE_OLD_FILES 参数,可以避免按下 y 并按 Enter 键删除每个文件。例如:

# make BATCH_DELETE_OLD_FILES=yes delete-old-libs

26.6.6.3. 更新后重启

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

# shutdown -r now

最后更新于