25.7. 重新编译 “world”
上一页 | 第 25 章 更新与升级 FreeBSD | 下一页

## 25.7. 重新编译 “world”

只要您根据一定版本的 FreeBSD (FreeBSD-STABLE、FreeBSD-CURRENT 等等)， 已经同步了您本地的源码树，那么您就可以使用这些源码树来重建系统。

### 做好备份:

无需强调在行动 之前 备份整个系统是多么的重要。 尽管重新编译系统是 (如果您按照文档的指示做的话) 一件很容易完成的工作， 但出错也是在所难免的， 另外， 别人在源码里面引入的错误也可能造成系统无法引导。

请确信自己已经做过备份， 并且在手边有恢复软盘或可以引导的光盘。 您可能永远也不会用到它， 但安全第一嘛！

### 订阅恰当的邮件列表:

FreeBSD-STABLE 和 FreeBSD-CURRENT 分支自然是 发展中的。为 FreeBSD 做贡献的都是人，偶尔也会犯错误。

有时这些错误没什么危害，只是引起您的系统生成新的诊断警告。 有时是灾难性的，并导致您的系统不能启动或破坏您的文件系统 (甚至更糟)。

如果出现了类似的问题， 贴一封“小心(heads up)”帖到相关的邮件列表里， 讲清问题的本质以及受影响的系统。在问题解决后，再贴封“解除(all clear)”声明。

如果使用 FreeBSD-STABLE 或 FreeBSD-CURRENT 而又不阅读 FreeBSD-STABLE 邮件列表 和 FreeBSD-CURRENT 邮件列表 各自的邮件列表， 那么您是自找麻烦。

### 不要使用 `make world`:

许多较早的文档推荐使用 `make world` 来完成这项工作。 这样做会跳过一些必要的步骤， 因此只有在您知道自己在做什么的时候才可以这样做。 几乎所有的情况下 `make world` 都是不应该做的事情， 您应该使用这里描述的方法。

### 25.7.1. 更新系统的规范方法

在更新系统时， 一定要首先查看 `/usr/src/UPDATING` 文件， 以便了解在 buildworld 之前需要进行的操作， 然后按照下面列出的步骤进行操作：

这些更新步骤假定您使用的是包含旧编译器、 内核以及用户态工具及配置的旧版 FreeBSD。 我们使用 “world” 来表示系统中的核心执行文件、 函数库和程序文件。 编译器是 “world” 的一部分， 但有其特殊性。

此外， 我们还假定您已经获得了较新版本操作系统的源代码。 如果您正更新的系统中的源代码也是旧版系统所附带的， 您还需要参阅 第 25.6 节 “同步您的源码” 来把代码同步到较新的版本。

从源代码更新系统， 有时会比初看上去的时候更麻烦一些， 另一方面， FreeBSD 的开发人员有时会不得不修改推荐的更新步骤， 特别是当出现了一些无法避免的依赖关系的时候。 这一节余下的部分， 将介绍目前推荐的更新步骤背后的原理。

成功的更新操作必须解决下面的这些问题：

- 旧的编译器可能无法编译新的内核。 (另一方面， 旧的编译器很可能有 bug。) 因此， 新的内核应该以新的编译器编译。 更具体地说， 新的编译器应在新内核开始联编之前已经完成了联编步骤。 请注意， 新的编译器并不一定需要在联编新内核之前 安装 到系统中。

- 新的 world 有可能依赖一些新的内核特性。 因此， 新内核必须在新的 world 之前安装。

这两个问题就是为什么我们将在后面的章节中介绍的， 需要按照 `buildworld`、 `buildkernel`、 `installkernel`、 `installworld` 的顺序来更新系统的原因。 这并不是您需要遵守推荐的更新操作的全部原因， 除了这两个最重要的理由之外， 还有一些并不那么显而易见的原因：

- 旧的 world 可能无法配合新的内核正常工作， 因此， 您在安装完新内核之后， 应尽快将 world 也随之更新。

- 有些配置文件的变动必须在安装新的 world 之前完成， 而另一些配置文件的变动则有可能导致旧 world 工作不正常。 因此， 通常而言会需要两次不同的配置文件更新步骤。

- 多数情况下， 更新步骤只会替换或增加文件； 换言之， 现有的旧文件并不会被删除。 有时， 这可能会导致一些其他问题。 因此， 有时安装操作会指明， 必须在某些操作之前手工删除一些文件。 这些在未来可能会被自动化， 也可能不会自动化。

由于有这些考虑， 因此一般情况下我们建议使用下列更新步骤。 请注意， 具体的更新操作中可能会需要一些附加的步骤， 但核心的过程应该是不会轻易发生变化的：

1. `make `buildworld``

   这步操作会联编新的编译器， 以及少量相关工具， 并在随后使用新的编译器来联编 world。 联编的结果会存放在 `/usr/obj`。

2. `make `buildkernel``

   与旧式的、 使用 config(8) 和 make(1) 的方法不同， 这种做法会使用存放于 `/usr/obj` 中的 新的 编译器。 这种做法使得您免去了由于编译器与内核源代码不一致导致的问题。

3. `make `installkernel``

   安装新的内核及其模块， 使系统能够以更新后的内核启动。

4. 重启系统并进入单用户模式。

   单用户模式使得更新正在运行的软件可能导致的问题减到最少。 此外， 它也使配合新内核运行旧 world 可能出现的问题减到最少。

5. `mergemaster `-p``

   这步操作会进行完成安装新的 world 所需的配置文件更新操作。 例如， 它可能会在系统的密码数据库中添加新的用户组或用户。 这些操作通常在上次更新之后增加了新的用户组或特殊系统用户之后是需要的， 因为 `installworld` 这步操作会需要这些用户或组才能顺利完成。

6. `make `installworld``

   从 `/usr/obj` 中复制 world。 这步操作之后， 您在盘上的系统， 包括内核和 world 就都是新的了。

7. `mergemaster`

   更新余下的配置文件， 因为您的 world 已经更新完成了。

8. 重启系统。

   这步操作将加在新的内核， 以及新的 world 和更新过的配置文件。

注意， 如果您正从同一 FreeBSD 版本分支升级， 例如， 从 7.0 到 7.1， 则上述过程可能没有那么必要， 因为您不太可能遇到严重的编译器、 内核源代码、 用户态程序源代码或配置文件不匹配的情形。 旧式的 `make `world`` 然后再联编新内核的升级方法， 很可能有机会能够正常运作而完成升级工作。

但是， 在大版本升级的过程中， 不按照前面所介绍的操作来进行升级时， 便很可能遇到一些问题。

此外， 还需要注意的是， 有些时候升级的过程中 (例如从 4.`X` 到 5.0) 可能会需要一些额外的步骤 (例如在 installworld 之前更名或删除一些文件)。 请仔细阅读 `/usr/src/UPDATING` 这个文件， 特别是它的结尾部分所介绍的推荐的升级操作顺序。

由于开发人员发现不可能完全避免一些不匹配方面的问题， 这个过程一直在演化过程中。 不过幸运的是， 目前推荐的这个升级步骤， 应该能够在很长一段时间内不需要做任何调整。

总结一下， 目前推荐的从源代码升级 FreeBSD 的方法是：

`#` `cd /usr/src` `#` `make buildworld` `#` `make buildkernel` `#` `make installkernel` `#` `shutdown -r now`

### 注意:

有时， 可能需要额外地执行一次 `mergemaster -p` 才能够完成 `buildworld` 步骤。 这些要求， 会在 `UPDATING` 中进行描述。 一般而言， 您可以简单地跳过这一步， 只要进行的不是大跨度的 FreeBSD 版本升级。

在 `installkernel` 成功完成之后， 您需要引导到单用户模式 (举例而言， 可以在加载器提示后输入 `boot -s`)。 接下来执行：

`#` `adjkerntz -i` `#` `mount -a -t ufs` `#` `mergemaster -p` `#` `cd /usr/src` `#` `make installworld` `#` `mergemaster` `#` `reboot`

### 阅读进一步的说明:

前面所给出的， 只是帮助您开始工作的简要说明。 要清楚地理解每一步， 特别是如果打算自行定制内核配置， 就应阅读下面的内容。

### 25.7.2. 阅读 `/usr/src/UPDATING`

在您做其它事之前，请阅读 `/usr/src/UPDATING` (或在您的源码里的等效的文件)。 这个文件要包含有关于您可能遇到的问题的重要信息， 或指定了您可能使用到的命令的执行顺序。如果 `UPDATING` 与您这里读到相矛盾，那就先依据 `UPDATING`。

### 重要:

正如先前所述，阅读 `UPDATING` 并不能替代订阅正确的邮件列表。两都是互补的，并不彼此排斥。

### 25.7.3. 检查 `/etc/make.conf`

检查 `/usr/share/examples/etc/make.conf` 以及 `/etc/make.conf`。 第一个文件包含了一些默认的定义 – 它们中的绝大多数都注释掉了。 为了在重新编译系统时能够使用它们， 请把这些选项加入到 `/etc/make.conf`。 请注意在 `/etc/make.conf` 中的任何设置同时也会影响每次运行 `make` 的结果， 因此设置一些适合自己系统的选项是一个好习惯。

一般的用户通常会从 `/usr/share/examples/etc/make.conf` 复制 `CFLAGS` 和 `NO_PROFILE` 这样的设置到 `/etc/make.conf` 中并令它们生效。

请考虑其他的一些选项 (例如 `COPTFLAGS`、 `NOPORTDOCS` 等等)， 看看是否合用。

### 25.7.4. 更新 `/etc` 里的文件

`/etc` 目录包含有除了您的系统启动时执行的脚本外大部分的系统配置信息。 有些脚本随 FreeBSD 的版本而不同。

有些配置文件在天天运行的系统里也是要使用到的。尤其是 `/etc/group`。

偶尔， 作为安装过程的一部分， `make installworld` 会要求事先创建某些特定的用户或组。 在进行升级时， 它们可能并不存在。 这会给升级造成问题。 有时， `make buildworld` 会检查它们是否已经存在。

最近就有个这样的例子， 当时新增了 `smmsp` 用户。 当用户尝试完成安装操作时， 在 mtree(8) 尝试建立 `/var/spool/clientmqueue` 时失败了。

解决办法是通过使用 `-p` 选项以构建前 (pre-buildworld) 模式运行 mergemaster(8)。 这表示只对比那些对于成功执行 `buildworld` 或 `installworld` 起关键作用的文件。 在第一次这样做时， 如果使用的是早期的不支持 `-p` 的 `mergemaster` 版本的话， 使用源码中的新版本即可。

`#` `cd /usr/src/usr.sbin/mergemaster` `#` `./mergemaster.sh -p`

### 提示:

如果您是个偏执狂 (paranoid)， 您可以检查您的系统看看哪个文件属于您已更名或删除了的那个组。

`#` `find / -group `GID` -print`

将显示所有 `GID` 组 (可以是组名也可以是数字地组 ID)所有的文件。

### 25.7.5. 改为单用户模式

您可能想在单用户模式下编译系统。 除了对更快处理事情显然有好处外， 重装系统将触及许多重要的系统文件， 包括所有标准系统二进制文件、库文件、包含 (include) 文件等等。 在正运行的系统 (尤其是在有活跃的用户的时候) 中更改这些文件是自寻烦恼。

另一种模式是在多用户模式下编译系统，然后转换到单用户模式下安装。 如果您喜欢这种方式，只需在建立 (build) 完成后才执行下边的步骤。 您推迟转换到单用户模式下直到您必须 `installkernel` 或 `installworld`。

从运行的系统里，以超级用户方式执行：

`#` `shutdown now`

这样就会转换到单用户模式。

除此之外， 也可以重启系统， 并在启动菜单处选择 “single user”(单用户) 选项。 这样系统将以单用户模式启动。 接着， 在 shell 提示符处执行：

`#` `fsck -p` `#` `mount -u /` `#` `mount -a -t ufs` `#` `swapon -a`

这会检查文件系统，重新将 `/` 以读/写模式挂接， 参考 `/etc/fstab` 挂接其它所有的 UFS 文件系统，然后启用交换区。

### 注意:

如果您的 CMOS 时钟是设置为本地时间，而不是 GMT (如果 date(1) 命令输出不能显示正确的时间和地区也确有其事)， 您可能也需要执行下边的命令：

`#` `adjkerntz -i`

这样可以确定您正确的本地时区设置—不这样做， 您以后可能会碰到一些问题。

### 25.7.6. 删除 `/usr/obj`

随着重新构建系统的进行， 编译结果会放到 (默认情况下) `/usr/obj` 下。 这些目录会映射到 `/usr/src`。

通过删除这个目录， 可以加速 `make buildworld` 的过程， 并避免相互依赖关系等复杂的问题。

`/usr/obj` 中的某些文件可能设置了不可改标记 (详情参见 chflags(1))， 需要首先去掉这些标志。

`#` `cd /usr/obj` `#` `chflags -R noschg *` `#` `rm -rf *`

### 25.7.7. 重新编译基本系统

#### 25.7.7.1. 保存输出

建议把执行 make(1) 后得到的输出存成一个文件。 如果什么地方出了错，您就会有个错误信息的备份。 尽管这样不能帮您分析哪里出了错， 但如果您把您的问题贴到某个邮件列表里就能帮助其他的人。

这样做最简单的办法是使用 script(1) 命令，同是带上参数指定存放输出的文件名。 您应在重建系统之前立即这样做，然后在过程完成时输入 `exit`。

`#` `script /var/tmp/mw.out` Script started, output file is /var/tmp/mw.out `#` `make TARGET` … compile, compile, compile … `#` `exit` Script done, …

如果您这样做，就 不要 把文件存到 `/tmp` 里边。下次启动时，这个目录就会被清除掉。 存放的最好地方是 `/var/tmp` (如上个实例)或 `root` 的主目录。

#### 25.7.7.2. 编译基本系统

您必须在`/usr/src`目录里边：

`#` `cd /usr/src`

(当然，除非您的源码是在其它地方，真是这样的话更换成那个目录就行了)。

使用 make(1) 命令重建系统。这个命令会从 `Makefile` (描述组成 FreeBSD 的程序应该怎样被重建， 以什么样的顺序建立等等) 里读取指令。

输入的一般命令格式如下：

`#` `make -`x` -D`VARIABLE` `target``

这个例子里，`-`x`` 是会传递给 make(1) 的一个选项。查看 make(1) 手册有您可用的选项例子。

`-D`VARIABLE`` 传递一个变量给 `Makefile`。这些变量控制了 `Makefile` 的行为。这些同 `/etc/make.conf` 设置的变量一样， 只是提供了另一种设置它们的方法。

`#` `make -DNO_PROFILE `target``

是另一种指定不被建立 (built) 的先定库 (profiled libraries) 的方式，协同 `/etc/make.conf` 里的

NO_PROFILE= true # 避免编译性能分析库

一起使用。

`目标 (target)` 告诉 make(1) 什么该做。每个 `Makefile` 定义了一定数量不同的“目标 (targets)”， 然后您选择的目标就决定了什么会发生。

有些目标列在 `Makefile` 里的，但并不意味着您要执行。相反，建立过程 (build process) 利用它们把重建系统的一些必要的步骤分割成几个子步骤。

大部分的时间不需要向 make(1) 传递参数，因此您的命令看起来可能象这样：

`#` `make `target``

此处 `target` 表示的是若干编译选项。 多数情况下， 第一个 target 都应该是 `buildworld`。

正如名字所暗示的，`buildworld` 在 `/usr/obj` 下边建立了一个全新的树， 然后使用另一个 target， `installworld` 在当前的机器里安装它。

将这些选项分开有两个优点。 首先， 它允许您安全地完成建立 (build)， 而不对正在运行的系统的组件产生影响。 构建过程是 “自主的 (self hosted)”。 因为这样， 您可以安全地在以多用户模式运行的机器里执行 `buildworld` ，而不用当心不良影响。 但是依然推荐您在单用户模式时运行 `installworld`。

第二，允许您使用 NFS 挂接 (NFS mounts) 升级您网络里的多台计算机。如果您有三台 `A`、`B` 和 `C` 想进行升级，在`A` 执行 `make buildworld` 和 `make installworld`。 然后将 `A` 上的 `/usr/src` 和 `/usr/obj` 通过 NFS 挂接到 `B` 和 `C` 上， 接下来， 只需在 `B` 和 `C` 上使用 `make installworld` 来安装构建的结果就可以了。

尽管 `world` target 仍然存在，强烈建议您不要用它。

运行

`#` `make buildworld`

我们提供了一个试验性的功能， 可以在构建过程中为 `make` 指定 `-j` 参数， 令其在构建过程中同时启动多个并发的进程。 对于多 CPU 的机器而言， 这样做有助于发挥其性能。 不过， 由于编译过程中的瓶颈主要是在 IO 而不是 CPU 上， 因此它也会对单 CPU 的机器带来好处。

对典型的单 CPU 机器， 可以使用：

`#` `make -j4 buildworld`

这样， make(1) 会最多同时启动 4 个进程。 从发到邮件列表中的经验看， 这样做能带来最佳的性能。

如果您使用的机器有多颗 CPU， 并且配置了 SMP 的内核， 也可以试试看 6 到 10 的数值， 并观察是否能带来构建性能上的改善。

#### 25.7.7.3. 耗时

联编基本系统所需的时间会受到很多因素的影响， 不过， 较新的机器应该都能在一两个小时之内完成 FreeBSD-STABLE 源代码的构建， 而无须任何技巧或捷径。 完成 FreeBSD-CURRENT 源代码的联编， 则通常需要更长一些的时间。

### 25.7.8. 编译和安装新内核

要充分利用您的新系统，您应该重新编译内核。 这是很有必要的，因为特定的内存结构已经发生了改变，像 ps(1) 和 top(1) 这样的程序会不能工作， 除非内核同源码树的版本是一样的。

最简单、最安全的方式是 build 并安装一个基于 `GENERIC` 的内核。虽然 `GENERIC` 可能没有适合您的系统的所有必要的设备， 但它包括了启动您的系统到单用户模式所必需的内容。 这是个不错的检测新系统是否工作正常的测试。在从 `GENERIC` 启动、核实系统可以工作后， 您就可以建立 (build) 一个基于您的正常内核配置文件的新的内核了。

在 FreeBSD 中， 首先完成 build world 然后再编译新内核非常重要。

### 注意:

如果您想建立一个定制内核，而且已经有了配置文件， 只需象这样使用 `KERNCONF=`MYKERNEL`：`

`#` `cd /usr/src` `#` `make buildkernel KERNCONF=`MYKERNEL`` `#` `make installkernel KERNCONF=`MYKERNEL``

注意，如果您已把 `内核安全级别(kern.securelevel)` 调高到了 1 以上，而且还设置了 `noschg` 或相似的标识到了您的内核二进制里边，您可能会发现转换到单用户模式里使用 `installkernel` 是很有必要的。 如果您没有设置它， 则应该也能毫无问题地在多用户模式执行这两个命令。 请参考 init(8) 以了解更多关于 `内核安全级(kern.securelevel)` 的信息；查看 chflags(1) 了解更多关于不同文件标识的信息。

### 25.7.9. 重启到单用户模式

您应该单用户模式测试新内核。照第 25.7.5 节 “改为单用户模式”处的说明去做。

### 25.7.10. 安装编译好的新系统

您现在应使用 `installworld` 来安装新的系统二进制。

执行

`#` `cd /usr/src` `#` `make installworld`

### 注意:

如果在 `make buildworld` 的命令行指定了变量，您就必须在 `make installworld` 命令行里指定同样的变量。 对于其它的选项并不是必需的，如，`-j` 就不能同 `installworld` 一起使用。

举例，您执行了：

`#` `make -DNO_PROFILE buildworld`

您就必须使用：

`#` `make -DNO_PROFILE installworld`

来安装结果，否则就要试着安装先定 (profiled) 的在 `make buildworld` 阶段没有建立 (built) 的二进制文件。

### 25.7.11. 不是由 `make installworld` 更新的更新文件

重新编译整个系统不会使用新的或改过的配置文件更新某些目录 (尤其像 `/etc`、`/var` 和 `/usr`)

更新这些文件最简单的方式就是使用 mergemaster(8)，手工去做也是可以的，只要您愿意。 不管您选择哪一种，一定记得备份 `/etc` 以防出错。

#### 25.7.11.1. `mergemaster`

贡献者：Tom Rhodes.

mergemaster(8) 工具是个 Bourne 脚本，用于检测 `/etc` 和 `/usr/src/etc` 源码树里边的配置文件的不同点。 这是保持系统配置文件同源码树里的一起更新的推荐方式。

在提示符里简单地输入 `mergemaster` 就可以开始，并观看它的开始过程。`mergemaster` 会建立一个临时的根(root)环境，在 `/` 下， 放置各种系统配置文件。这些文件然后同当前安装到您系统里的进行比较。 此时，不同的文件会以 diff(1) 格式进行显示，使用 `+` 符号标识增加或修改的行，`-` 标识将完全删除的行或将被替换成新行。查看 diff(1) 手册可以得到更多关于 diff(1) 语法和文件不同点怎样显示的信息。

mergemaster(8) 会给您显示每个文件的不同处， 这样您就可以选择是删除新文件 (相对临时文件)， 是以未改状态安装临时文件，是以当前安装的文件合并临时文件， 还是再看一次 diff(1) 结果。

“选择删除临时文件”将使 mergemaster(8) 知道我们希望保留我们当前的文件不改，并删除新的。 并不推荐这个选择，除非您没有更改当前文件的理由。任何时候在 mergemaster(8) 提示符里输入 ?，您就会得到帮助。 如果选择跳过文件，将在其它文件处理完后再次进行。

“选择安装未修改临时文件”将会使新文件替换当前的。 对大部分未改的文件，这是个最好的选择。

“选择合并文件”将为您打开一个文本编辑器， 里边是两个文件的内容。您现在就可以一边合并它们， 一边在屏幕里查看，同时从两者中选取部分生成最终文件。 当两个文件一起比较时，l 键会选择左边的内容， r 会选择右边的。最终的输出是由两个部分组成的一个文件， 用它就可以安装了。这个选项通常用于用户修改了设置的文件。

“选择再次查看 diff(1) 结果”将会在提供给选择之前， 显示文件的不同处，就象 mergemaster(8) 所做的一样。

在 mergemaster(8) 完成了对系统文件的处理后， 您会得到其它的选项。mergemaster(8) 可能会问您是否要重建密码文件， 并在最后提示您是否要删除余下的临时文件。

#### 25.7.11.2. 手动更新

如果想要手工更新，但不要只是从 `/usr/src/etc` 把文件复制到 `/etc` 就了事。有些文件是必须先“安装”的。 这是因为 `/usr/src/etc` 目录并 不是 想像的那样是 `/etc` 目录的一个复制。事实上，有些是文件是 `/etc` 有的，而 `/usr/src/etc` 里边没有。

如果您使用 mergemaster(8) (作为推荐)，您可以向前跳到 下一节。

手工做最简单的方式是安装这些文件到一个新的目录，完成后再来查找不同处。

### 备份您已有的 `/etc`:

虽然，理论上，没有什么会自动访问这个目录， 事情还是做稳操胜当一点。复制已有 `/etc` 到一个安全的地方，如：

`#` `cp -Rp /etc /etc.old`

`-R` 完成递归复制 (译者注：即可以复制目录以下的所有内容)，`-p` 保留文件的时间、所属等等。

您需要建立一个虚目录 (a dummy set of directories) 来安装新的 `/etc` 和其它文件。 `/var/tmp/root` 是个不错的选择， 除此之外，还有一些子目录是需要的。

`#` `mkdir /var/tmp/root` `#` `cd /usr/src/etc` `#` `make DESTDIR=/var/tmp/root distrib-dirs distribution`

这样就建好了需要的目录结构，然后安装文件。在 `/var/tmp/root` 下建立的大部分子目录是空的， 而且要删除掉。最简单的方式是：

`#` `cd /var/tmp/root` `#` `find -d . -type d | xargs rmdir 2>/dev/null`

这样会删除所有的空目录。(标准的错误信息被重定向到了 `/dev/null`，以防止关于非空目录的警告。)

`/var/tmp/root` 现在包含了应放在 `/` 下某个位置的所有文件。 您现在必须仔细检查每一个文件，检测它们与您已有的文件有多大不同。

注意，有些已经安装在 `/var/tmp/root` 下的文件有个“.”在开头。在写的时候，像这样唯一的文件是 `/var/tmp/root/` 和 `/var/tmp/root/root/` 里 shell 启动文件，尽管可能有其它的(依赖于您什么时候读取这个)。 确信使用 `ls -a` 可以看到它们。

最简单的方式是使用 diff(1) 去比较两个文件：

`#` `diff /etc/shells /var/tmp/root/etc/shells`

这会显示出 `/etc/shells` 文件和新的 `/var/tmp/root/etc/shells` 文件的不同处。 用这些来决定是合并您已做的变化还是复制您的旧文件过来。

### 使用日戳 (Time Stamp) 命名新的 Root(根)目录(`/var/tmp/root`)，这样您可以轻松地比较两个版本的不同:

频繁重建系统意味着必须频繁更新 `/etc`，而这可能会有点烦琐。

在合并到 `/etc` 的文件里， 最新更改的您可以做个复制，由此加快这个(指更新)过程。 下边就给出了一个怎样做的主意。

1. 像平常一样建立系统 (Make the world)。当您想更新 `/etc` 和其它目录里， 给目标目录一个含有当前日期的名字。假如您是 1998 年 2 月 14 日做的，您可以执行下边的：

   `#` `mkdir /var/tmp/root-19980214` `#` `cd /usr/src/etc` `#` `make DESTDIR=/var/tmp/root-19980214 \ distrib-dirs distribution`

2. 如上边列出的，从这个目录合并变化。

   在您完成后，不要 删除 `/var/tmp/root-19980214` 目录。

3. 在您下载了最新版的源码并改过后，执行第一步。 这样将得到一个新的目录，可能叫做 `/var/tmp/root-19980221` (如果等了一周做的升级)。

4. 您现在能看到两个目录间的不同了---在隔周的时间里使用 diff(1) 建立递归 diff 产生的不同：

   `#` `cd /var/tmp` `#` `diff -r root-19980214 root-19980221`

   一般情况下，这两种间的不同处比 `/var/tmp/root-19980221/etc` 和 `/etc` 之间的不同要小很多。 因为不同点更小，也就更容易把这些变化移到您的 `/etc` 目录里边。

5. 您现在可以删除早先的两个 `/var/tmp/root-*` 目录：

   `#` `rm -rf /var/tmp/root-19980214`

6. 每次您需要合并这些变化到 `/etc` 里，就重复这个流程。

您可以使用 date(1) 自动产生目录的名称：

`#` `mkdir /var/tmp/root-`date "+%Y%m%d"``

### 25.7.12. 重启

现在完成了。在您检查所有内容都放置正确后， 您可以重启系统了。只是简单的 shutdown(8) 可以这样做：

`#` `shutdown -r now`

### 25.7.13. 结束

恭喜！您现在成功升级了您的 FreeBSD 系统。

如果还有轻微的错误，可以轻易地重建系统的选定部分。 例如，在部分升级或合并 `/etc` 时，您不小心删除了 `/etc/magic`，file(1) 命令就会停止工作。这种情况下，执行下边进行修复：

`#` `cd /usr/src/usr.bin/file` `#` `make all install`

### 25.7.14. 问题

25.7.14.1. 每个变化您都须要重建系统吗？
25.7.14.2. 我的编译失败，并伴随有许多 11 (或其它的数字信息) 号错误。是怎么回事呀？
25.7.14.3. 我完成后可以删除 /usr/obj 吗？
25.7.14.4. 中断的 build 可以被恢复吗？
25.7.14.5. 我怎样加快建立系统的速度？
25.7.14.6. 如果出现了错误我该怎么办？

25.7.14.1. | 每个变化您都须要重建系统吗？

这个不好说，因为要看变化的情况。如，如果您刚运行了 CVSup，并得到下边更新的文件：

`src/games/cribbage/instr.c` `src/games/sail/pl_main.c` `src/release/sysinstall/config.c` `src/release/sysinstall/media.c` `src/share/mk/bsd.port.mk`

这就不必重建整个系统。您只需到相关的子目录里执行 `make all install`，仅此而已。 但是，如果有重大变化，如 `src/lib/libc/stdlib`， 那么您就要重建系统或至少静态连接的那些部分 (除了您增加的部分都是静态连接的)。

在这天后，就是您的事了。要是说每两个星期重建一下系统的话， 您可能会高兴。或者您可能只想重做改变过的部分， 确信您能找出所有依赖关系。

当然，所有这些依赖于您想升级的频率，和您是否想跟踪 FreeBSD-STABLE 或 FreeBSD-CURRENT。

25.7.14.2. | 我的编译失败，并伴随有许多 11 (或其它的数字信息) 号错误。是怎么回事呀？
| 这个通常表示硬件错误。 (重)建系统是个强压测试系统硬件的有效地方式， 并且常常产生内存错误。 这些正好表示它们自已做为编译器离奇地死于收到的奇怪信息。 一个确信的指示器是如果重新开始 make，并且整个过程中会死在不同的点上。 对于这种情况，您没有什么可做的，除了更换机器里的部件，看是哪一个坏了。
25.7.14.3. | 我完成后可以删除 `/usr/obj` 吗？
| 简短地说，可以。 `/usr/obj` 包含了所有在编译阶段生成的目标文件。通常， 在 `make buildworld` 过程中第一步之一就是删除这个目录重新开始。 这种情况下，在您完成后，保留 `/usr/obj` 没有多大意义，还可释放一大堆磁盘空间(目前是 2 GB 左右)。 不过， 如果您很了解整个过程， 也可以让 `make buildworld` 跳过这一步。 这会让后续的联编过程执行得更快， 因为大部分的源码都不必再进行编译了。 这样做的负面效果是它可能会触发一些由于敏感的依赖关系导致的问题， 这些问题会导致联编以奇怪的方式出错并失败。 这在 FreeBSD 邮件列表里经常引起沸腾， 当有人抱怨他们 build 失败时，并没意识到这是因为自已是想抄近路。
25.7.14.4. | 中断的 build 可以被恢复吗？

依赖于您在您找到问题之前整个过程进行了多远。

一般而言 (当然这并不是硬性规定)， `make buildworld` 的过程中将会首先构建新版的基本构建工具 (例如 gcc(1)， 以及 make(1)) 和系统库。 随后会安装这些工具和库。 这些新版本的工具和库在随后将被用于重新编译和连接它们本身。 整个系统 (现在包括了常规的用户程序， 例如 ls(1) 或 grep(1)) 会同新版的系统文件一起被重新构建。

如果您正处于最后一个阶段， 并且了解它 (因为您已经看过了所保存的输出) 则可以 (相当安全地) 做：

… 问题修复 … `#` `cd /usr/src` `#` `make -DNO_CLEAN all`

这样就不会取消先前的 `make buildworld` 所做的工作了。

在“make buildworld”的输出中如果看到如下信息：

-------------------------------------------------------------- Building everything.. --------------------------------------------------------------

出现在 `make buildworld` 的输出中， 则这样做应该不会有什么问题。

如果没有看到这样的信息， 或者您不确定， 则从头开始构建将是万无一失的做法。

25.7.14.5. | 我怎样加快建立系统的速度？

- 以单用户模式运行

- 把 `/usr/src` 和 `/usr/obj` 目录放到不同磁盘里的独立文件系统里。如果可能，这些磁盘在不同的磁盘控制器里。

- 更好的，是把这些文件系统放置到多个使用 ccd(4) (连接磁盘驱动器--concatenated disk driver)设备的磁盘里。

- 关掉 profiling (在 `/etc/make.conf` 里设置 “NO_PROFILE=true”)。您差不多用不了它。

- 在 `/etc/make.conf` 里也为 `CFLAGS` 设置上 `-O -pipe`。 最佳优化 `-O2` 会更慢，而且 `-O` 和 `-O2` 之间的优化差别基本上可以忽略。 `-pipe` 让编译器使用管道而不用临时文件进行通信， 这样可以减少磁盘存取 (以内存作为代价)。

- 传递 `-j`n`` 选项给 make(1) 以便并发运行多个进程。 这样就不会考虑您的是否是单个或多个处理器机器。

- 存放 `/usr/src` 的文件系统可以使用 `noatime` 选项来挂接 (或重新挂接)。 这样会防止文件系统记录文件的存取时间。 您可能并不需要这些信息。

  `#` `mount -u -o noatime /usr/src`

  ### 警告:

  这个例子里假定 `/usr/src` 是在它自已的文件系统里。如果不是 (例如假设它是 `/usr` 的部分)，那么您就需要那个文件系统挂接点， 而不是 `/usr/src`。

- 存放 `/usr/obj` 的文件系统可以使用 `async` 选项被挂接 (或重新挂接)。 这样做将启用异步写盘。 换句话说， 对应用程序而言写会立即完成， 而数据则延迟几秒才会写到盘里。 这样做能够成批地写下数据， 从而极大地改善性能。

  ### 警告:

  注意， 这个选项会使您的文件系统变得脆弱。 使用这个选项会提高在电源断掉或机器非正常重启时， 文件系统进入不可恢复状态的概率。

  如果在这个文件系统里 `/usr/obj` 是很关键的，这不是问题。如果您有其它有价值的数据在同一个文件系统， 那么在您使用这个选项这前，确认备份一下。

  `#` `mount -u -o async /usr/obj`

  ### 警告:

  同上，如果 `/usr/obj` 不在自已的文件系统里，使用相关挂接点的名字把它从例子里边替换掉。

25.7.14.6. | 如果出现了错误我该怎么办？
| 绝对确信您的环境没有先前 build 留下的残余。这点够简单。 `#` `chflags -R noschg /usr/obj/usr` `#` `rm -rf /usr/obj/usr` `#` `cd /usr/src` `#` `make cleandir` `#` `make cleandir` 不错，`make cleandir` 真的要执行两次。 然后重新开始整个过程，使用 `make buildworld` 开始。 如果您还有问题，就把错误和 `uname -a` 的输出发送到 FreeBSD 一般问题邮件列表 邮件列表。准备回答其它关于您的设置的问题！

上一页 | 上一级 | 下一页
25.6. 同步您的源码 | 起始页 | 25.8. 删除过时的文件、 目录和函数库

本文档和其它文档可从这里下载： ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题，请先阅读 文档，如不能解决再联系 <questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.