# 26.6.从源代码更新 FreeBSD

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

## 26.6.1. 快速开始

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

> **警告**
>
> 从 [mergemaster(8)](https://man.freebsd.org/cgi/man.cgi?query=mergemaster\&sektion=8\&format=html) 切换到 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 时，第一次运行可能会错误地合并更改，生成虚假的冲突。为防止这种情况，在更新源代码并构建新版本之前，执行以下步骤：
>
> ```sh
> # etcupdate extract ①
> # etcupdate diff    ②
> ```
>
> * ① 引导 **/etc** 文件的数据库；有关更多信息，请参见 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html)。
> * ② 引导数据库后检查差异。修剪任何不再需要的本地更改，以减少未来更新时发生冲突的可能性。

* 更新和构建

  ```sh
  # 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        ⑪
  ```
* ① 获取最新的源代码版本。有关获取和更新源代码的更多信息，请参见 [更新源代码](https://docs.freebsd.org/en/books/handbook/cutting-edge/#updating-src-obtaining-src)。
* ② 检查 **/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 版本控制系统。验证源代码是否已纳入版本控制：

```sh
# 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)](https://man.freebsd.org/cgi/man.cgi?query=git\&sektion=1\&format=html) 更新：

```sh
# 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)](https://man.freebsd.org/cgi/man.cgi?query=uname\&sektion=1\&format=html) 确定正在使用的 FreeBSD 版本：

```sh
# uname -r   
13.2-RELEASE 
```

根据 [FreeBSD 版本和仓库分支](https://docs.freebsd.org/en/books/handbook/cutting-edge/#updating-src-obtaining-src-repopath)，更新 `13.2-RELEASE` 的源代码使用的仓库路径为 `releng/13.2`。此路径在克隆源代码时使用：

```sh
# mv /usr/src /usr/src.bak ①
# git clone --branch releng/13.2 https://git.FreeBSD.org/src.git /usr/src ②
```

* ① 将旧目录移到其他位置。如果该目录中没有本地修改，可以删除它。
* ② [FreeBSD 版本和仓库分支](https://docs.freebsd.org/en/books/handbook/cutting-edge/#updating-src-obtaining-src-repopath) 中的路径已添加到仓库 URL 中。第三个参数是源代码在本地系统上的目标目录。

## 26.6.4. 从源代码构建

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

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

编译后的代码会写入 **/usr/obj** 目录。

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

### 26.6.4.1. 执行干净的构建

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

```sh
# make cleanworld
```

### 26.6.4.2. 设置构建任务的数量

在多核处理器上增加构建任务的数量可以提高构建速度。使用 `sysctl hw.ncpu` 来确定核心数量。由于处理器和不同版本的 FreeBSD 使用的构建系统有所不同，因此测试是唯一能确定不同任务数量对构建速度影响的可靠方法。作为起点，考虑使用核心数量的一半到两倍。可以使用 `-j` 来指定任务数量。

**示例 1. 增加构建任务数量**

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

```sh
# make -j4 buildworld buildkernel
```

### 26.6.4.3. 仅构建内核

如果源代码发生更改，必须先完成 `buildworld`。之后，可以随时运行 `buildkernel` 来构建内核。要仅构建内核：

```sh
# cd /usr/src
# make buildkernel
```

### 26.6.4.4. 构建自定义内核

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

例如，对于一个开发小型嵌入式计算机的人员，若其 RAM 严重受限，可以删除不需要的设备驱动程序或选项，以使内核稍微小一些。

内核配置文件位于 **/usr/src/sys/arch/conf/** 目录中，其中 *arch* 是 `uname -m` 命令的输出。在大多数计算机上，`arch` 为 `amd64`，因此配置文件目录为 **/usr/src/sys/amd64/conf/**。

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

可以通过复制 **GENERIC** 配置文件来创建一个自定义配置文件。例如，假设新的自定义内核是用于存储服务器，因此命名为 **STORAGESERVER**：

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

然后编辑 **/root/STORAGESERVER**，根据 [config(5)](https://man.freebsd.org/cgi/man.cgi?query=config\&sektion=5\&format=html) 添加或删除设备或选项。

通过在命令行中设置 `KERNCONF` 来构建自定义内核：

```sh
# make buildkernel KERNCONF=STORAGESERVER
```

## 26.6.5. 安装编译后的代码

完成 `buildworld` 和 `buildkernel` 步骤后，安装新的内核和 world：

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

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

```sh
# 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)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 合并配置文件

[etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 是一个用于管理文件更新的工具，这些文件不作为 `installworld` 的一部分进行更新，例如位于 **/etc/** 目录中的文件。它通过对这些文件进行三方合并，将对文件所做的更改与本地版本进行比对。[etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 旨在最小化用户干预的次数。

> **注意**
>
> 一般情况下，[etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 不需要任何特定的参数来完成任务。然而，有一个方便的中间命令可以用于在首次使用 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 时进行健康检查：
>
> ```sh
> # etcupdate diff
> ```
>
> 此命令允许用户审计配置更改。

如果 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 无法自动合并某个文件，则可以通过手动交互来解决合并冲突，方法是运行：

```sh
# etcupdate resolve
```

> **警告**
>
> 从 [mergemaster(8)](https://man.freebsd.org/cgi/man.cgi?query=mergemaster\&sektion=8\&format=html) 切换到 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html) 时，首次运行可能会错误地合并更改，从而生成虚假的冲突。为了避免这种情况，请在更新源代码并构建新系统前执行以下步骤：
>
> ```sh
> # etcupdate extract ①
> # etcupdate diff    ②
> ```
>
> * ① 引导 **/etc** 文件的数据库；更多信息，请参见 [etcupdate(8)](https://man.freebsd.org/cgi/man.cgi?query=etcupdate\&sektion=8\&format=html)。
> * ② 在引导后检查 diff。修剪任何不再需要的本地更改，以减少未来更新时的冲突可能性。

### 26.6.6.2. 检查过时的文件和库

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

```sh
# make check-old
```

并删除：

```sh
# make delete-old
```

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

```sh
# make check-old-libs
```

并通过以下命令删除：

```sh
# make delete-old-libs
```

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

> **技巧**
>
> 当确认所有旧文件或目录可以安全删除时，通过设置 `BATCH_DELETE_OLD_FILES` 参数，可以避免按下 `y` 并按 Enter 键删除每个文件。例如：
>
> ```sh
> # make BATCH_DELETE_OLD_FILES=yes delete-old-libs
> ```

### 26.6.6.3. 更新后重启

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

```sh
# shutdown -r now
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://handbook.bsdcn.org/di-26-zhang-freebsd-geng-xin-yu-sheng-ji/26.6.-cong-yuan-dai-ma-geng-xin-freebsd.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
