---
title: 第 25 章 更新与升级 FreeBSD
part: 部分 III. 系统管理
prev: books/handbook/l10n
next: books/handbook/dtrace
---

[[updating-upgrading]]
= 更新与升级 FreeBSD
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:source-highlighter: rouge
:experimental:
:skip-front-matter:
:toc-title: 目录
:table-caption: 表
:figure-caption: 图
:example-caption: 例
:xrefstyle: basic
:relfileprefix: ../
:outfilesuffix:
:sectnumoffset: 25

ifeval::["{backend}" == "html5"]
:imagesdir: ../../../images/books/handbook/cutting-edge/
endif::[]

ifeval::["{backend}" == "pdf"]
:imagesdir: ../../../../static/images/books/handbook/cutting-edge/
endif::[]

ifeval::["{backend}" == "epub3"]
:imagesdir: ../../../../static/images/books/handbook/cutting-edge/
endif::[]

include::shared/authors.adoc[]
include::shared/releases.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/urls.adoc[]

toc::[]

[[updating-upgrading-synopsis]]
== 概述

FreeBSD 在发行版之间始终是持续开发的。 一些人喜欢使用官方发行的版本， 另一些喜欢与最新的开发保持同步。 然而， 即使是官方的发行版本也常常需要安全补丁和重大修正方面的更新。 不论你使用了何种版本， FreeBSD 都提供了所有更新系统所需的工具， 让你轻松的在不同版本间升级。 这一章节将帮助你决定是跟踪开发系统还是坚持使用某个发行的版本。 同时还列出了一些保持系统更新所需的基本工具。

读了本章后，您将了解到：

* 使用哪些工具来更新系统与 Ports Collection。
* 如何使用 freebsd-update, CVSup, CVS, or CTM 让你的系统保持更新。
* 如何比较已安装的系统与原来已知拷贝的状态。
* 如何使用 CVSup 或者文档 ports 来更新本地的文档。
* 两个开发分支 FreeBSD-STABLE 和 FreeBSD-CURRENT 的区别。
* 如何通过 `make buildworld` 重新编译安装整个基本系统(等等)。

在读本章这前，您应该了解的：

* 正确设置网络连接 (crossref:advanced-networking[advanced-networking,高级网络])。
* 知道怎样安装附加的第三方软件(crossref:ports[ports,安装应用程序. Packages 和 Ports])。

[NOTE]
====
整个这一章中，`cvsup` 命令都被用来获取 FreeBSD 源代码的更新。 你需要安装 package:net/cvsup[] port 或者二进制包(如果你不想要安装图形界面的 `cvsup` 客户端的话， 则可以安装 package:net/cvsup-without-gui[] port)。 你也可以使用 man:csup[1] 代替， 它现在已经是基本系统的一部分了。
====

[[updating-upgrading-freebsdupdate]]
== FreeBSD 更新

打安全补丁是对于维护计算机软件的一个重要部分， 特别是对于操作系统。对于 FreeBSD 来说， 很长的一段时间以来这都不是一件容易的事情。 补丁打在源代码上，代码需要被重新编译为二进制， 然后再重新安装编译后的程序。

FreeBSD 引入了 `freebsd-update` 工具之后这便不再是问题了。这个工具提供了 2 种功能。 第一，它可以把二进制的安全和勘误更新直接应用于 FreeBSD 的基本系统，而不需要重新编译和安装。第二， 这个工具还支持主要跟次要的发行版的升级。

[NOTE]
====
由安全小组支持的各种体系结构和发行版都可使用二进制更新。 在升级到一个新的发行版本之前， 应先阅读一下当前发行版的声明， 因为它们可能包含有关于你期望升级版本的重要消息。 这些发行声明可以通过以下链接查阅： http://www.FreeBSD.org/releases/[http://www.FreeBSD.org/releases/]。
====

如果 `crontab` 中存在有用到 `freebsd-update` 特性的部分， 那么这些在开始以下操作前必须先被禁止。

[[freebsdupdate-config-file]]
=== 配置文件

有些用户可能希望通过调整配置文件 [.filename]#/etc/freebsd-update.conf# 中的默认配置来更好地控制升级的过程。 可用的参数在文档中介绍的很详细， 但下面的这些可能需要进一步的解释：

[.programlisting]
....
# Components of the base system which should be kept updated.
Components src world kernel
....

这个参数是控制 FreeBSD 的哪一部分将被保持更新。 默认的是更新源代码，整个基本系统还有内核。 这些部件跟安装时的那些相同，举例来说， 在这里加入 `world/games` 就会允许打入游戏相关的补丁。 使用 `src/bin` 则是允许更新 [.filename]#src/bin# 目录中的源代码。

最好的选择是把这个选项保留为默认值， 因为如果要修改它去包含一些指定的选项， 就需要用户列出每一个想要更新的项目。 这可能会引起可怕的后果， 因为部分的源代码和二进制程序得不到同步。

[.programlisting]
....
# Paths which start with anything matching an entry in an IgnorePaths
# statement will be ignored.
IgnorePaths
....

添加路径，比如 [.filename]#/bin# 或者 [.filename]#/sbin# 让这些指定的目录在更新过程中不被修改。 这个选项能够防止本地的修改被 `freebsd-update` 覆盖。

[.programlisting]
....
# 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` 在合并时保存修改。

[.programlisting]
....
# 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/
....

一个 `freebsd-update` 应该尝试合并的配置文件的列表。文件合并的过程是 一系列的 man:diff[1] 补丁类似于更少选项的 man:mergemaster[8] 合并的选项是接受，打开一个文本编辑器，或者 `freebsd-update` 会被中止。 在不能确定的时候，请先备份 [.filename]#/etc# 然后接受合并。更多关于 `mergemaster` 的信息请参阅 <<mergemaster>>。

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

这个目录是放置所有补丁和临时文件的。 用户做一个版本升级的话，请确认此处至少有 1 GB 的可用磁盘空间。

[.programlisting]
....
# 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` 会尝试更新 `Componets` 列表里的每一个文件。

[[freebsdupdate-security-patches]]
=== 安全补丁

安全补丁存储在远程的机器上， 可以使用如下的命令下载并安装：

[source,shell]
....
# freebsd-update fetch
# freebsd-update install
....

如果给内核打了补丁，那么系统需要重新启动。 如果一切都进展顺利，系统就应该被打好了补丁而且 `freebsd-update` 可由夜间 man:cron[8] 执行。在 [.filename]#/etc/crontab# 中加入以下条目足以完成这项任务：

[.programlisting]
....
@daily                                  root    freebsd-update cron
....

这条记录是说明每天运行一次 `freebsd-update` 工具。 用这种方法， 使用了 `cron` 参数， `freebsd-update` 仅检查是否存在更新。 如果有了新的补丁，就会自动下载到本地的磁盘， 但不会自动给系统打上。`root` 会收到一封电子邮件告知需手动安装补丁。

如果出现了错误，可以使用下面的 `freebsd-update` 命令回退到上一次的修改：

[source,shell]
....
# freebsd-update rollback
....

完成以后如果内核或任何的内核模块被修改的话， 就需要重新启动系统。这将使 FreeBSD 装载新的二进制程序进内存。

`freebsd-update` 工具只能自动更新 [.filename]#GENERIC# 内核。 如果您使用自行联编的内核， 则在 `freebsd-update` 安装完更新的其余部分之后需要手工重新联编和安装内核。 不过， `freebsd-update` 会检测并更新位于 [.filename]#/boot/GENERIC# (如果存在) 中的 [.filename]#GENERIC# 内核， 即使它不是当前 (正在运行的) 系统的内核。

[NOTE]
====
保存一份 [.filename]#GENERIC# 内核的副本到 [.filename]#/boot/GENERIC# 是一个明智的主意。 在诊断许多问题， 以及在 <<freebsdupdate-upgrade>> 中介绍的使用 `freebsd-update` 更新系统时会很有用。
====

除非修改位于 [.filename]#/etc/freebsd-update.conf# 中的配置， `freebsd-update` 会随其他安装一起对内核的源代码进行更新。 重新联编并安装定制的内核可以以通常的方式来进行。

[NOTE]
====
通过 `freebsd-update` 发布的更新有时并不会涉及内核。 如果在执行 `freebsd-update install` 的过程中内核代码没有进行变动， 就没有必要重新联编内核了。 不过， 由于 `freebsd-update` 每次都会更新 [.filename]#/usr/src/sys/conf/newvers.sh# 文件， 而修订版本 (`uname -r` 报告的 `-p` 数字) 来自这个文件， 因此， 即使内核没有发生变化， 重新联编内核也可以让 man:uname[1] 报告准确的修订版本。 在维护许多系统时这样做会比较有帮助， 因为这一信息可以迅速反映机器上安装的软件更新情况。
====

[[freebsdupdate-upgrade]]
=== 重大和次要的更新

这个过程会删除旧的目标文件和库， 这将使大部分的第三方应用程序无法删除。 建议将所有安装的 ports 先删除然后重新安装，或者稍后使用 package:ports-mgmt/portupgrade[] 工具升级。 大多数用户将会使用如下命令尝试编译：

[source,shell]
....
# portupgrade -af
....

这将确保所有的东西都会被正确的重新安装。 请注意环境变量 `BATCH` 设置成 `yes` 的话将在整个过程中对所有询问回答 `yes`，这会帮助在编译过程中免去人工的介入。

如果正在使用的是定制的内核， 则升级操作会复杂一些。 您会需要将一份 [.filename]#GENERIC# 内核的副本放到 [.filename]#/boot/GENERIC#。 如果系统中没有 [.filename]#GENERIC# 内核， 可以用以下两种方法之一来安装：

* 如果只联编过一次内核， 则位于 [.filename]#/boot/kernel.old# 中的内核， 就是 [.filename]#GENERIC# 的那一个。 只需将这个目录改名为 [.filename]#/boot/GENERIC# 即可。
* 假如能够直接接触机器， 则可以通过 CD-ROM 介质来安装 [.filename]#GENERIC# 内核。 将安装盘插入光驱， 并执行下列命令：
+
[source,shell]
....
# mount /cdrom
# cd /cdrom/X.Y-RELEASE/kernels
# ./install.sh GENERIC
....
+ 
您需要将 [.filename]#X.Y-RELEASE# 替换为您正在使用的版本。 [.filename]#GENERIC# 内核默认情况下会安装到 [.filename]#/boot/GENERIC#。
* 如果前面的方法都不可用， 还可以使用源代码来重新联编和安装 [.filename]#GENERIC# 内核：
+
[source,shell]
....
# cd /usr/src
# env DESTDIR=/boot/GENERIC make kernel
# mv /boot/GENERIC/boot/kernel/* /boot/GENERIC
# rm -rf /boot/GENERIC/boot
....
+ 
如果希望 `freebsd-update` 能够正确地将内核识别为 [.filename]#GENERIC#， 您必须确保没有对 [.filename]#GENERIC# 配置文件进行过任何变动。 此外， 建议您取消任何其他特殊的编译选项 (例如使用空的 [.filename]#/etc/make.conf#)。

上述步骤并不需要使用这个 [.filename]#GENERIC# 内核来引导系统。

重大和次要的更新可以由 `freebsd-update` 命令后指定一个发行版本来执行， 举例来说，下面的命令将帮助你升级到 FreeBSD 8.1：

[source,shell]
....
# freebsd-update -r 8.1-RELEASE upgrade
....

在执行这个命令之后，`freebsd-update` 将会先解析配置文件和评估当前的系统以获得更新系统所需的必要信息。 然后便会显示出一个包含了已检测到与未检测到的组件列表。 例如：

[source,shell]
....
Looking up update.FreeBSD.org mirrors... 1 mirrors found.
Fetching metadata signature for 8.0-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` 将会尝试下载所有升级所需的文件。在某些情况下， 用户可能被问及需安装些什么和如何进行之类的问题。

当使用定制内核时， 前面的步骤会产生类似下面的警告：

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

此时您可以暂时安全地无视这个警告。 更新的 [.filename]#GENERIC# 内核将在升级过程的中间步骤中使用。

在下载完针对本地系统的补丁之后， 这些补丁会被应用到系统上。 这个过程需要消耗的时间取决于机器的速度和其负载。 这个过程中将会对配置文件所做的变动进行合并 - 这一部分需要用户的参与， 文件可能会自动合并， 屏幕上也可能会给出一个编辑器， 用于手工完成合并操作。 在处理过程中， 合并成功的结果会显示给用户。 失败或被忽略的合并， 则会导致这一过程的终止。 用户可能会希望备份一份 [.filename]#/etc# 并在这之后手工合并重要的文件， 例如 [.filename]#master.passwd# 和 [.filename]#group#。

[NOTE]
====
系统至此还没有被修改，所有的补丁和合并都在另外一个目录中进行。 当所有的补丁都被成功的打上了以后，所有的配置文件都被合并后， 我们就已经完成了整个升级过程中最困难的部分， 下面就需要用户来安装这些变更了。
====

一旦这个步骤完成后，使用如下的命令将升级后的文件安装到磁盘上。

[source,shell]
....
# freebsd-update install
....

内核和内核模块会首先被打上补丁。 此时必须重新启动计算机。 如果您使用的是定制的内核， 请使用 man:nextboot[8] 命令来将下一次用于引导系统的内核 [.filename]#/boot/GENERIC# (它会被更新)：

[source,shell]
....
# nextboot -k GENERIC
....

[WARNING]
====

在使用 [.filename]#GENERIC# 内核启动之前， 请确信它包含了用于引导系统所需的全部驱动程序 (如果您是在远程进行升级操作， 还应确信网卡驱动也是存在的)。 特别要注意的情形是， 如果之前的内核中静态联编了通常以内核模块形式存在的驱动程序， 一定要通过 [.filename]#/boot/loader.conf# 机制来将这些模块加载到 [.filename]#GENERIC# 内核的基础上。 此外， 您可能也希望临时取消不重要的服务、 磁盘和网络挂载等等， 直到升级过程完成为止。
====

现在可以用更新后的内核引导系统了：

[source,shell]
....
# shutdown -r now
....

在系统重新上线后，需要再次运行 `freebsd-update`。 升级的状态被保存着，这样 `freebsd-update` 就无需重头开始，但是会删除所有旧的共享库和目标文件。 执行如下命令继续这个阶段的升级：

[source,shell]
....
# freebsd-update install
....

[NOTE]
====
取决与是否有库的版本更新，通常只有 2 个而不是 3 个安装阶段。
====

现在需要重新编译和安装第三方软件。 这么做的原因是某些已安装的软件可能依赖于在升级过程中已删除的库。 可使用 package:ports-mgmt/portupgrade[] 自动化这个步骤，以如下的命令开始：

[source,shell]
....
# portupgrade -f ruby
# rm /var/db/pkg/pkgdb.db
# portupgrade -f ruby18-bdb
# rm /var/db/pkg/pkgdb.db /usr/ports/INDEX-*.db
# portupgrade -af
....

一旦这个完成了以后，再最后一次运行 `freebsd-update` 来结束升级过程。 执行如下命令处理升级中的所有细节：

[source,shell]
....
# freebsd-update install
....

如果您临时用过 [.filename]#GENERIC# 内核来引导系统， 现在是按照通常的方法重新联编并安装新的定制内核的时候了。

重新启动机器进入新版本的 FreeBSD 升级过程至此就完成了。

[[freebsdupdate-system-comparison]]
=== 系统状态对照

`freebsd-update` 工具也可被用来对着一个已知完好的 FreeBSD 拷贝测试当前的版本。 这个选项评估当前的系统工具，库和配置文件。 使用以下的命令开始对照：

[source,shell]
....
# freebsd-update IDS >> outfile.ids
....

[WARNING]
====

这个命令的名称是 IDS， 它并不是一个像 package:security/snort[] 这样的入侵检测系统的替代品。因为 `freebsd-update` 在磁盘上存储数据， 很显然它们有被篡改的可能。 当然也可以使用一些方法来降低被篡改的可能性，比如设置 `kern.securelevel` 和不使用时把 `freebsd-update` 数据放在只读文件系统上，例如 DVD 或 安全存放的外置 USB 磁盘上。
====

现在系统将会被检查，生成一份包含了文件和它们的 man:sha256[1] 哈希值的清单，已知发行版中的值与当前系统中安装的值将会被打印到屏幕上。 这就是为什么输出被送到了 [.filename]#outfile.ids# 文件。 它滚动的太块无法用肉眼对照，而且会很快填满控制台的缓冲区。

这个文件中有非常长的行，但输出的格式很容易分析。 举例来说，要获得一份与发行版中不同哈希值的文件列表， 已可使用如下的命令：

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

这份输出时删节缩短后的，其实是有更多的文件。 其中有些文件并非人为修改，比如 [.filename]#/etc/passwd# 被修改是因为添加了用户进系统。在某些情况下， 还有另外的一些文件，诸如内核模块与 `freebsd-update` 的不同是因为它们被更新过了。 为了指定的文件或目录排除在外，把它们加到 [.filename]#/etc/freebsd-update.conf# 的 `IDSIgnorePaths` 选项中。

除了前面讨论过的部分之外， 这也能被当作是对升级方法的详细补充。

[[updating-upgrading-portsnap]]
== Portsnap： 一个 Ports Collection 更新工具

FreeBSD 基本系统也包括了一个更新 Ports Collection 的工具： man:portsnap[8]。在运行之后，它会连上一个远程网站， 校验安全密钥，然后下载一份 Ports Collection 的拷贝。 密钥是用来校验所有下载文件的完整性，确保它们在传输是未被修改。 使用以下的命令下载最新的 Ports Collection：

[source,shell]
....
# portsnap fetch
Looking up portsnap.FreeBSD.org mirrors... 3 mirrors found.
Fetching snapshot tag from portsnap1.FreeBSD.org... done.
Fetching snapshot metadata... done.
Updating from Wed Aug  6 18:00:22 EDT 2008 to Sat Aug 30 20:24:11 EDT 2008.
Fetching 3 metadata patches.. done.
Applying metadata patches... done.
Fetching 3 metadata files... done.
Fetching 90 patches.....10....20....30....40....50....60....70....80....90. done.
Applying patches... done.
Fetching 133 new ports or files... done.
....

这个例子展示的是 man:portsnap[8] 发现并校验了几个用于当前 ports 的补丁。这还表明以前运行过， 如果是第一次运行的话，那么仅仅只会下载 Ports Collection。

在 man:portsnap[8] 成功地完成一次 `fetch` 操作之后， 会将校验过的 Ports 套件和后续的补丁保存在本地。 首次执行 `portsnap` 之后， 你必须使用 `extract` 安装下载的文件：

[source,shell]
....
# portsnap extract
/usr/ports/.cvsignore
/usr/ports/CHANGES
/usr/ports/COPYRIGHT
/usr/ports/GIDs
/usr/ports/KNOBS
/usr/ports/LEGAL
/usr/ports/MOVED
/usr/ports/Makefile
/usr/ports/Mk/bsd.apache.mk
/usr/ports/Mk/bsd.autotools.mk
/usr/ports/Mk/bsd.cmake.mk
...
....

使用 `portsnap update` 命令更新已安装的 Ports：

[source,shell]
....
# portsnap update
....

至此更新就完成了，然后便可以使用更新后的 Ports Collection 来安装或升级应用程序。

`fetch` 和 `extract` 或 `update` 可以作为连续的动作执行， 如下例所示：

[source,shell]
....
# portsnap fetch update
....

这个命令将会下载最新版本的 Ports 并更新本地位于 [.filename]#/usr/ports# 的拷贝。

[[updating-upgrading-documentation]]
== 更新系统附带的文档

除了基本系统和 Ports 套件之外， 文档也是 FreeBSD 操作系统的一个组成部分。 尽管您总是可以通过 http://www.freebsd.org/doc/[FreeBSD 网站] 来访问最新的 FreeBSD 文档， 一些用户的网络连接可能很慢， 甚至完全没有网络连接。 幸运的是， 有很多方法可以用来更新随发行版本附带的 FreeBSD 文档的本地副本。

[[csup-doc]]
=== 使用 CVSup 来更新文档

FreeBSD 文档的源代码和安装版本都可以通过 CVSup 来以与基本系统 (参考 <<makeworld>>) 类似的方法来升级。 这一节中将会介绍：

* 如何安装联编文档所需的工具集， 用于从源代码来联编 FreeBSD 文档所需的那些工具。
* 如何使用 CVSup 将文档下载到 [.filename]#/usr/doc#。
* 如何从源代码联编 FreeBSD 文档， 并将其安装到 [.filename]#/usr/shared/doc#。
* 联编文档的过程中支持的一些编译选项， 例如只联编某些语言的版本， 或只联编特定的输出格式。

[[installing-documentation-toolchain]]
=== 安装 CVSup 和文档工具集

从源代码联编 FreeBSD 文档需要大量的工具。 这些工具并不是 FreeBSD 基本系统的一部分， 因为这些工具需要占用大量的磁盘空间， 而且并不是对所有 FreeBSD 用户都有用； 只有活跃地撰写 FreeBSD 新文档， 或经常从源代码更新文档的用户才需要这些工具。

全部所需的工具， 均可通过 Ports 套件来安装。 package:textproc/docproj[] port 是由 FreeBSD 文档计划开发的方便安装和更新这些工具的主 port。

[NOTE]
====
如果不需要 PostScript(R) 或 PDF 文档的话， 也可以考虑安装 package:textproc/docproj-nojadetex[] port。 这套文档工具集包含除了 teTeX typesetting 引擎之外的其他全部工具。 teTeX 是一个很大的工具集， 因此如果不需要 PDF 输出的话， 排除它会节省很多时间和磁盘空间。
====

如欲了解关于安装和使用 CVSup 的进一步信息， 请参阅 crossref:mirrors[cvsup,使用 CVSup]。

[[updating-documentation-sources]]
=== 更新文档源代码

CVSup 工具能够下载文档源代码的原始副本， 您可使用 [.filename]#/usr/shared/examples/cvsup/doc-supfile# 文件作为配置模板来修改。 在 [.filename]#doc-supfile# 中的默认主机名是一个无效的占位主机名， 但 man:cvsup[1] 能够通过命令行来指定主机名， 因此文档源代码可以使用下面的命令从 CVSup 服务器获得：

[source,shell]
....
# cvsup -h cvsup.FreeBSD.org -g -L 2 /usr/shared/examples/cvsup/doc-supfile
....

您应将 _cvsup.FreeBSD.org_ 改为最近的 CVSup 服务器。 参见 crossref:mirrors[cvsup-mirrors,CVSup 站点] 关于镜像站点的完整列表。

初始的文档源代码下载需要一些时间， 您需要耐心等待它完成。

后续的更新可以用同样的命令来进行。 由于 CVSup 工具只下载上次运行之后所发生过的更新， 因此在首次运行之后再运行 CVSup 应该是很快的。

在签出源代码之后， 还可以使用另一种由 [.filename]#/usr/doc# 目录中的 [.filename]#Makefile# 支持的方法来更新它。 通过在 [.filename]#/etc/make.conf# 中配置 `SUP_UPDATE`、 `SUPHOST` 和 `DOCSUPFILE`， 可以通过运行：

[source,shell]
....
# cd /usr/doc
# make update
....

来完成更新。 典型的 [.filename]#/etc/make.conf# 中的 man:make[1] 选项是：

[.programlisting]
....
SUP_UPDATE= yes
SUPHOST?= cvsup.freebsd.org
DOCSUPFILE?= /usr/shared/examples/cvsup/doc-supfile
....

[NOTE]
====
将 `SUPHOST` 和 `DOCSUPFILE` 的值使用 `?=` 来指定的好处是使 make 命令行能够覆盖这些选项。 在向 [.filename]#make.conf# 中增加选项时推荐这样做， 以避免在测试时反复修改这个文件。
====

[[updating-documentation-options]]
=== 文档源代码中可调的选项

FreeBSD 文档的更新和联编系统支持一些方便只更新一部分文档， 或只联编特定格式及译文的选项。 这些选项可以在 [.filename]#/etc/make.conf# 文件中配置， 也可以通过 man:make[1] 工具来指定。

这些选项包括：

`DOC_LANG`::
准备联编和安装的语言列表。 例如， 指定为 `en_US.ISO8859-1` 表示只联编英文版的文档。

`FORMATS`::
准备输出的格式列表。 目前， 系统支持 `html`、 `html-split`、 `txt`、 `ps`、 `pdf`、 和 `rtf`。

`SUPHOST`::
用于用来更新的 CVSup 服务器的主机名。

`DOCDIR`::
用于安装文档的目录。 默认为 [.filename]#/usr/shared/doc#。

如欲了解 FreeBSD 中其他可供配置的全局 make 变量， 请参阅 man:make.conf[5]。

关于 FreeBSD 文档联编系统的其他详情， 请参阅 link:{fdp-primer}[FreeBSD 文档计划入门之新手必读部分]。

[[updating-installed-documentation]]
=== 从源代码安装 FreeBSD 文档

在 [.filename]#/usr/doc# 中下载了最新的文档源代码快照之后， 就可以开始动手联编文档了。

要更新全部 `DOC_LANG` 中定义的语言的文档， 需要执行下面的命令：

[source,shell]
....
# cd /usr/doc
# make install clean
....

如果在 [.filename]#make.conf# 中配置了正确的 `DOCSUPFILE`、 `SUPHOST` 和 `SUP_UPDATE` 选项， 则可以将更新源代码和安装一步完成：

[source,shell]
....
# cd /usr/doc
# make update install clean
....

如果只需要更新某个特定语言的文档， 可以在 [.filename]#/usr/doc# 中与之对应的目录中运行 man:make[1]：

[source,shell]
....
# cd /usr/doc/en_US.ISO8859-1
# make update install clean
....

此外， 还可以透过 make 变量 `FORMATS` 来控制输出格式， 例如：

[source,shell]
....
# cd /usr/doc
# make FORMATS='html html-split' install clean
....

[[doc-ports]]
=== 使用文档 Ports

在之前的章节中， 我们已展示了从源代码更新 FreeBSD 文档的方法。 基于源代码的更新的方法可能并不是对于所有的 FreeBSD 系统都可行有效。 编译文档源代码需要一大堆的工具， _文档工具链_， 对于 CVS 的一定了解和从仓库中检出源代码， 还有一些编译已检出代码的手工步骤。 这一章节我们将介绍一种使用 Ports 来更新已安装的 FreeBSD 文档：

* 下载并安装预编译好的文档快照， 而不用在本地编译任何部份 (这样便不再需要安装整个文档工具链了)。
* 下载文档的源代码并使用 ports 框架编译 (使得检出和编译的步骤更容易些)。

这两种更新 FreeBSD 文档的方法都由一组 {doceng} 每月更新的 _文档 ports_ 提供支持。 这些都列在了 FreeBSD Ports http://www.freshports.org/docs/[docs] 虚拟分类下面。

[[doc-ports-install-make]]
==== 编译和安装文档 Ports

文档 ports 使用 ports 的构建框架使得文档的编译变得更加容易。 自动化了检出文档源代码， 配以适合的环境设置和命令行参数运行 man:make[1]， 它们使得安装或卸载文档变得就像安装 FreeBSD 其他 port 或二进制包那样容易。

[NOTE]
====
另一个特性便是当在本地编译文档 ports 时， _文档工具链_ ports 会被列入依赖关系， 并自动安装。
====

文档 ports 按以下的方式组织：

* 一个 "主 port"， 在 package:misc/freebsd-doc-en[] 下可以找到这个文档 port。 它是所有文档 ports 的基础。 在默认的情况下， 它只安装英文版文档。
* 一个 "合集 port"， package:misc/freebsd-doc-all[]， 它将构建并安装所有语言版本的所有文档。
* 最后是各种翻译的 "从属 port"， 比如： package:misc/freebsd-doc-hu[] 是匈牙利文版的文档。 所有这些都基于主 port 并会安装上对应语言的翻译文档。

以 `root` 用户身份运行如下的命令安装文档：

[source,shell]
....
# cd /usr/ports/misc/freebsd-doc-en
# make install clean
....

这将会安装分章节的英文版本 HTML 格式文档 (与link:http://www.FreeBSD.org[http://www.FreeBSD.org] 上的相同) 到 [.filename]#/usr/local/shared/doc/freebsd# 目录。

[[doc-ports-options]]
===== 常见的调节选项

文档 ports 有许多用来修改默认行为的选项。 以下是一段简要列表：

`WITH_HTML`::
允许构建 HTML 格式： 每份文档为一个单一的 HTML 文件。 此种文档的文件名视情况而定通常是 [.filename]#article.html#， 或 [.filename]#book.html#， 另外附加一些图片。

`WITH_PDF`::
允许构建 Adobe(R) Portable Document Format， 可使用 Adobe(R) Acrobat Reader(R)， Ghostscript 或者其他的 PDF 阅读器查阅。 此种文档的文件名视情况而定通常是 [.filename]#article.pdf# 或 [.filename]#book.pdf#。

`DOCBASE`::
文档将被安装到的目录。默认值 [.filename]#/usr/local/shared/doc/freebsd#。
+

[NOTE]
====
请注意默认的目录与 CVSup 方法种所使用的目录不同。 这是因为我们正在安装的是一个 port， 而 ports 通常会被安装到 [.filename]#/usr/local# 目录。 这可以指定 `PREFIX` 变量覆盖默认值。
====

这是一份简短的关于如何使用以上提到变量来安装 PDF 格式的匈牙利文档：

[source,shell]
....
# cd /usr/ports/misc/freebsd-doc-hu
# make -DWITH_PDF DOCBASE=share/doc/freebsd/hu install clean
....

[[doc-ports-install-package]]
==== 使用文档 Packages

正如上文所述， 从 ports 构建文档需要在本地安装一份文档工具链和一些编译所需的磁盘空间。 当不够资源安装文档工具链， 或者从源代码编译需要太多的磁盘空间时， 我们仍然可以安装预编译好的文档快照的 ports。

{doceng} 每个月都会制作 FreeBSD 文档快照的包。 这些二进制包可以通过包工具来操作， 比如 man:pkg_add[1]， man:pkg_delete[1]， 等等。

[NOTE]
====
当使用二进制包时， 将安装所指定语言相关的 FreeBSD 文档的 _所有_ 可用格式。
====

举例来说， 以下的命令将安装最新预编译的匈牙利语文档：

[source,shell]
....
# pkg_add -r hu-freebsd-doc
....

[NOTE]
====
二进制包使用了以下与对应 ports 名称不同的命名格式: `lang-freebsd-doc`。 这里的 _lang_ 是语言代码的简短形式， 比如 `hu` 表示匈牙利语， 或者 `zh_cn` 表示简体中文。
====

[[doc-ports-update]]
==== 更新文档 Ports

任何用于更新 ports 的工具都可以被用来更新已安装的文档 port。 举例来说， 下面的命令通过 package:ports-mgmt/portupgrade[] 工具来更新已安装的匈牙利语文档二进制包。

[source,shell]
....
# portupgrade -PP hu-freebsd-doc
....

[[current-stable]]
== 追踪开发分支

FreeBSD 有两个开发分支： FreeBSD-CURRENT 和 FreeBSD-STABLE。 这一章节将对每个分支作相应介绍与如何保持你的系统更新。 我们将先介绍 FreeBSD-CURRENT 然后是 FreeBSD-STABLE。

[[current]]
=== 使用最新的 FreeBSD CURRENT

这里再次强调， FreeBSD-CURRENT 是 FreeBSD 开发的 "最前沿"。 FreeBSD-CURRENT 用户要有较高的技术能力， 并且应该有能力自已解决困难的系统问题。 如果您是个 FreeBSD 新手， 那么在安装之前最好三思。

==== FreeBSD-CURRENT 是什么？

FreeBSD-CURRENT 是 FreeBSD 的发展前沿。 包括了在下一个官方发行的软件中可能存在， 也可能不存在的发展、 试验性改动、 以及过渡性的机制。 尽管许多 FreeBSD 开发者每天都会编译 FreeBSD-CURRENT 源代码， 但有时这些代码仍然会是不能编译的。 虽然这些问题会很快解决， 但 FreeBSD-CURRENT 是带来破坏还是您正希望的功能性改善， 很可能完全取决于您获取源代码的的时机！

==== 谁需要 FreeBSD-CURRENT？

FreeBSD-CURRENT 适合下边三种主要兴趣团体：

. FreeBSD 社区的成员： 积极工作在源码树的某部分的人和为保持 "最新" 为绝对需求的人。
. FreeBSD 社区的成员： 为促使 FreeBSD-CURRENT 保持尽可能的健全而愿花时间去解决问题的积极的测试者； 以及那些愿意提出关于 FreeBSD 变化和总体方向的建设性建议并且提供补丁实现它们的人们。
. 那些只是想关注或为了参考目的使用当前 (current) 源码的人们 (如，为了__阅读__，而不是执行)。 这些人也偶尔做做注释或贡献代码。

==== FreeBSD-CURRENT __不是__什么？

. 追求最新功能， 您听说里面有一些很酷的新功能， 并希望成为您周围的人中第一个尝试它们的人。 尽管您能够因此首先了解到最新的功能， 但这也意味着在出现新的 bug 时您也首当其冲。
. 修复错漏的快捷方式。任何 FreeBSD-CURRENT 的既定版本在修复已知错漏的同时又可能会产生新的错漏。
. 无所不在的"官方支持"。 我们尽最大努力在3个"合法的" FreeBSD-CURRENT 组之一真诚给人们提供帮助，但是我们 __没有时间__提供技术支持。 这并不是因为我们是那种不喜欢帮助人解困的无耻之徒 (如果我们是的话，就不会制作 FreeBSD 了)。 我们不能每天简单地回复上百的消息，__而且__ 我们继续发展 FreeBSD！ 在改善 FreeBSD 和回复大量关于实验代码的问题之间如果要做个选择的话， 开发人员会选择前者。

==== 使用 FreeBSD-CURRENT

. 加入 {freebsd-current} 和 {svn-src-head} 列表。 这个不仅仅是个好主意，而且很 _重要_。如果您不去 _{freebsd-current}_， 您就不会看到人们所做的关于系统当前状态的说明， 这样您就有可能在别人已经发现并解决了的一大堆问题面前难倒。 更重要的是您会错过一些重要的公告---对于您的系统安全可能是至关重要的。
+ 
{svn-src-head} 列表允许您看到每个变化的提交记录， 因为这些记录与其它相关信息是同步的。
+ 
要加入这些列表，或其它可能的列表，请访问 {mailman-lists-url} ，并且点击您想订阅的列项。 关于其它步骤的说明那里有提供。 如果你有兴趣追踪整个原代码树的变更记录， 我们建议你订阅 {svn-src-all} 邮件列表。
. 从FreeBSD crossref:mirrors[mirrors,镜像站点] 获取源码。 您有两种方式选择：
..
+ 
与称作 [.filename]##standard-supfile## 的 [.filename]##supfile## 一起使用 crossref:mirrors[cvsup,cvsup]，这个可以从 [.filename]##/usr/shared/examples/cvsup##得到。 这是最被推荐的方式，因为它允许您一次获取整个集合， 以后就只取更改过的部分。许多人从 `cron` 运行 `cvsup`，以保持他们的源码自动更新。 您须要定制上边的 [.filename]##supfile## 样本，并且配置 crossref:mirrors[cvsup,cvsup] 以适应您的环境。
+
[NOTE]
====
[.filename]#standard-supfile# 例子是为追踪指定的 FreeBSD 安全分支而指定的， 而不是 FreeBSD-CURRENT。 你需要编辑这个文件并把如下这行：

[.programlisting]
....
*default release=cvs tag=RELENG_X_Y
....

替换为：

[.programlisting]
....
*default release=cvs tag=.
....

可以参阅手册中的 crossref:mirrors[cvs-tags,CVS Tags] 章节获得更多可用 tag 的详细说明。
====
+ 
使用工具 CTM。 如果您的连接性能不太好(高价连接或只能通过电子邮件存取)， CTM 是个选择。 但这也颇有争议并且常常得到到坏文件。因此很少使用它， 这也注定了不能长期用它来工作。对于使用 9600 bps 或更快连接的人，我们推荐使用 CVSup。 

. 如果您获取源码是用于运行，而不只是看看，那么就获取 _整个_ FreeBSD-CURRENT，不要选部分。 这样做的原因是源码的大部分都依赖于其他部分， 要是您试着只编译其中一部分的话，保证您会陷入麻烦。
+ 
在编译 FreeBSD-CURRENT 之前，请仔细阅读 [.filename]#/usr/src# 里的 [.filename]#Makefile# 文件。 尽管是部分的升级过程，您至少也要首先<<makeworld,安装新的内核和重建系统>>。阅读 {freebsd-current} 邮件列表和 [.filename]#/usr/src/UPDATING#， 会让您在其它循序渐进的过程中保持最新， 这对于我们向下一个发行版转移是很有必要的。
. 热心一点！如果您正运行 FreeBSD-CURRENT， 我们很想知道您关于它的一些想法， 尤其是关于错漏修复或增进的建议。 非常欢迎带有代码的建议！

[[stable]]
=== 使用最新的 FreeBSD STABLE

==== FreeBSD-STABLE 是什么?

FreeBSD-STABLE 是我们的发展分支，我们的主要发行版就由此而来。 这个分支会以不同速度变化，并且假定这些是第一次进入 FreeBSD-CURRENT 进行测试。然而，这 _仍然_ 是个发展中的分支，这意味着在一定的时候，FreeBSD-STABLE 源码可能或不可能满足一些特殊的要求。 它只不过是另一个工程发展途径，并不是终端用户的资源。

==== 谁需要 FreeBSD-STABLE？

如果您有兴趣追随 FreeBSD 的开发过程或为其做点贡献， 尤其是和下一个 "非计划" 的 FreeBSD 发行版有关时， 您应该考虑采用 FreeBSD-STABLE。

尽管安全更新也会进入 FreeBSD-STABLE 分支，但您并不 _必须_ 使用 FreeBSD-STABLE 来达到这样的目的。 每一个 FreeBSD 的安全公告都会解释如何修复受到影响的发行版中的问题 ，而因为安全原因而去采用一个开发分支显然可能会同时引入一些不希望的修改。

尽管我们尽力确保 FreeBSD-STABLE 分支在任何时候都能够正确编译和运行， 但没有人能够担保它在任何时候都总可以。 此外， 尽管代码在进入 FreeBSD-STABLE 之前都是在 FreeBSD-CURRENT 上完成开发， 但使用 FreeBSD-STABLE 的人要比使用 FreeBSD-CURRENT 的更多。 有证据显示， 犄角旮旯里的各种问题有些时候仍然会由于在 FreeBSD-CURRENT 不那么明显 而在 FreeBSD-STABLE 暴露出来。

基于这些原因， _不_ 推荐您盲目地追随 FreeBSD-STABLE， 并且， 在粗略地测试过代码之前不要更新任何生产服务器到 FreeBSD-STABLE 也非常重要。

如果您没有用于完成这些工作的资源， 我们推荐您使用最新的 FreeBSD 发行版， 并使用发行版提供的二进制更新机制来在发行版之间完成迁移。

==== 使用FreeBSD-STABLE

. 加入 {freebsd-stable} 列表。让您随时了解可能出现在 FreeBSD-STABLE 里的"build 依赖性"或其它需要特别注意的问题。 当开发员正在考虑某些有争议的修复或更新时， 他们就会在这个邮件列表里发表声明，给用户机会回应， 看他们对于提出的变化是否还有什么问题。
+ 
加入相关的 SVN 列表来追踪你所关心的分支。比如，如果你在追踪 7-STABLE 分支，加入 link:{svn-src-stable-7-url}[svn-src-stable-7] 列表。 这样每次这个分支上有改动的时候就能让你看到提交记录, 还包括了修改可能引起的副作用之类的相关信息。
+ 
要加入这些列表或其他可用的，访问 {mailman-lists-url} 并点击您希望订阅的列表。关于其它步骤的说明可以在那里看到。 如果你有兴趣追踪整个原代码树的变更记录， 我们建议你订阅 {svn-src-all} 邮件列表。
. 如果您正安装一个新系统， 并希望它运行每月从 FreeBSD-STABLE 编译的快照， 请察看 link:https://www.FreeBSD.org/snapshots/[ Snapshots] 网页以了解更多信息。 另外， 也可以从 crossref:mirrors[mirrors,镜像站点] 安装最新的 FreeBSD-STABLE 发行版， 并按照其中的说明将系统更新到最新的 FreeBSD-STABLE 源代码。
+ 
如果您已经在运行较早的 FreeBSD 版本， 并希望通过源代码方式升级， 则可以通过 FreeBSD crossref:mirrors[mirrors,镜像站点] 来完成。 这可以通过两种方式来进行：
..
+ 
与称作 [.filename]#stable-supfile# 的 [.filename]#supfile# 一起使用 crossref:mirrors[cvsup,cvsup]，这个可以从 [.filename]#/usr/shared/examples/cvsup# 得到。 这是最被推荐的方式，因为它允许您一次获取整个集合， 以后就只取更改过的部分。许多人从 `cron` 运行 `cvsup`，以保持他们的源码自动更新。 您须要定制上边的 [.filename]#supfile# 样本，并且配置 crossref:mirrors[cvsup,cvsup] 以适应您的环境。
..
+ 
使用工具 CTM。 如果您的连接性能不太好(高价连接或只能通过电子邮件存取)， CTM 是个选择。 但这也颇有争议并且常常得到到坏文件。因此很少使用它， 这也注定了不能长期用它来工作。对于使用 9600 bps 或更快连接的人，我们推荐使用 CVSup。 

. 本质上说，如果您需要快速存取源码并且不计较通信宽带的话，可以使用 `cvsup` 或 `ftp`。否则，就使用 CTM。
.
+ 
在编译 FreeBSD-STABLE 之前，请仔细阅读 [.filename]#/usr/src# 里的 [.filename]#Makefile#。 您至少应该<<makeworld,安装一个新的内核并重建系统>>， 首先做为升级过程的一部分。阅读 {freebsd-stable} 邮件列表和 [.filename]#/usr/src/UPDATING#， 可能让您在其它循序渐进的过程中保持更新， 这在我们向下一发行版转移时是很有必要的。

[[synching]]
== 同步您的源码

有许多方式通过互联网(或电子邮件)与 FreeBSD 项目源码特定领域或所有领域保持更新，主要依赖于您的兴趣。 我们提供的主要服务是crossref:mirrors[anoncvs,匿名 CVS]、 crossref:mirrors[cvsup,CVSup]，和 crossref:mirrors[ctm,CTM]。

[WARNING]
====

虽然只更新源码树中的部分是可能的， 唯一被支持的更新过程是更新整个树、并且重编译用户区 (如：在用户空间运行的所有程序，像 [.filename]##/bin## 和 [.filename]##/sbin##下边的)和内核源码。 只更新源码树中的部分，或只有内核，或只有用户区 (userland) 通常会出现错误。这些问题包括有编译错误、内核崩溃 (kernel panics)、数据出错。
====

匿名 CVS 和 CVSup 使用 _下拉(pull)_ 模式来更新源代码。 在 CVSup 中， 用户 (或者 `cron` 脚本) 会调用 `cvsup` 程序， 后者会同某一个 `cvsupd` 服务进行交互， 以更新您的文件。 您接到的更新是更新时刻最新的， 并且您只会收到那些需要的更新。 您可以很容易地限制更新的范围， 只更新那些您需要的文件。 服务器端会根据您手头已经有的文件即时地生成更新内容。 匿名 CVS 相对于 CVSup 而言要简单一些， 因为它只是对 CVS 的一种扩展， 让您可以从远程的 CVS 代码库得到更新。 CVSup 相对而言， 要比 匿名 CVS 更有效率， 然而后者却更容易使用。

另一种方法是 CTM。 这种方法并不能将您手头的代码与中央代码库中的版本进行比较， 也不能下载它们。 在主 CTM 服务器上运行的脚本会每天执行多次， 每次运行都能够自动地识别所有文件自上次运行以来所发生的变化， 如果发现有文件发生了变动， 就会压缩、 标上一个序列号， 并进行便于使用电子邮件进行传送的编码操作 (其中只包括可打印的 ASCII 字符)。 一旦接收到， 这些"CTM deltas"就会被传送给 man:ctm_rmail[1] 工具---可以自动进行解码、校验和应用这些变化到用户的复制的源码里。 这个过程比 CVSup 更为有效， 而且更少占用我们的服务器资源，因为它不仅仅采用 _下拉(pull)_ 模式，还采用 _上推(push)_ 模式。

当然， 这样做也会带来一些不便。 如果您不经意删除了您的压缩包的部分内容， CVSup 会检测到并为您重建破坏的部分。 CTM 是不会这样做的， 如果您删除了您的源码树中的某部分(并已不能恢复)， 那么您就必须从破坏处 (从最新的CVS "base delta") 开始，使用 CTM 或 匿名 CVS 进行重建，仅仅删除坏的数据并再同步。

[[makeworld]]
== 重新编译 "world"

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

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

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

[WARNING]
.订阅恰当的邮件列表
====
FreeBSD-STABLE 和 FreeBSD-CURRENT 分支自然是 _发展中的_。为 FreeBSD 做贡献的都是人，偶尔也会犯错误。

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

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

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

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

[[canonical-build]]
=== 更新系统的规范方法

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

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

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

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

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

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

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

* 旧的 world 可能无法配合新的内核正常工作， 因此， 您在安装完新内核之后， 应尽快将 world 也随之更新。
* 有些配置文件的变动必须在安装新的 world 之前完成， 而另一些配置文件的变动则有可能导致旧 world 工作不正常。 因此， 通常而言会需要两次不同的配置文件更新步骤。
* 多数情况下， 更新步骤只会替换或增加文件； 换言之， 现有的旧文件并不会被删除。 有时， 这可能会导致一些其他问题。 因此， 有时安装操作会指明， 必须在某些操作之前手工删除一些文件。 这些在未来可能会被自动化， 也可能不会自动化。

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

. `make buildworld`
+ 
这步操作会联编新的编译器， 以及少量相关工具， 并在随后使用新的编译器来联编 world。 联编的结果会存放在 [.filename]#/usr/obj#。
. `make buildkernel`
+ 
与旧式的、 使用 man:config[8] 和 man:make[1] 的方法不同， 这种做法会使用存放于 [.filename]#/usr/obj# 中的 _新的_ 编译器。 这种做法使得您免去了由于编译器与内核源代码不一致导致的问题。
. `make installkernel`
+ 
安装新的内核及其模块， 使系统能够以更新后的内核启动。
. 重启系统并进入单用户模式。
+ 
单用户模式使得更新正在运行的软件可能导致的问题减到最少。 此外， 它也使配合新内核运行旧 world 可能出现的问题减到最少。
. `mergemaster -p`
+ 
这步操作会进行完成安装新的 world 所需的配置文件更新操作。 例如， 它可能会在系统的密码数据库中添加新的用户组或用户。 这些操作通常在上次更新之后增加了新的用户组或特殊系统用户之后是需要的， 因为 `installworld` 这步操作会需要这些用户或组才能顺利完成。
. `make installworld`
+ 
从 [.filename]#/usr/obj# 中复制 world。 这步操作之后， 您在盘上的系统， 包括内核和 world 就都是新的了。
. `mergemaster`
+ 
更新余下的配置文件， 因为您的 world 已经更新完成了。
. 重启系统。
+ 
这步操作将加在新的内核， 以及新的 world 和更新过的配置文件。

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

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

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

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

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

[source,shell]
....
# cd /usr/src
# make buildworld
# make buildkernel
# make installkernel
# shutdown -r now
....

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

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

[source,shell]
....
# adjkerntz -i
# mount -a -t ufs
# mergemaster -p
# cd /usr/src
# make installworld
# mergemaster
# reboot
....

.阅读进一步的说明
[WARNING]
====

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

[[src-updating]]
=== 阅读 [.filename]#/usr/src/UPDATING#

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

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

[[make-conf]]
=== 检查 [.filename]#/etc/make.conf#

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

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

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

[[updating-etc]]
=== 更新 [.filename]#/etc# 里的文件

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

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

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

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

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

[source,shell]
....
# cd /usr/src/usr.sbin/mergemaster
# ./mergemaster.sh -p
....

[TIP]
====

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

[source,shell]
....
# find / -group GID -print
....

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

[[makeworld-singleuser]]
=== 改为单用户模式

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

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

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

[source,shell]
....
# shutdown now
....

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

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

[source,shell]
....
# fsck -p
# mount -u /
# mount -a -t ufs
# swapon -a
....

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

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

[source,shell]
....
# adjkerntz -i
....

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

[[cleaning-usr-obj]]
=== 删除 [.filename]#/usr/obj#

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

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

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

[source,shell]
....
# cd /usr/obj
# chflags -R noschg *
# rm -rf *
....

[[updating-upgrading-compilebase]]
=== 重新编译基本系统

==== 保存输出

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

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

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

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

[[make-buildworld]]
==== 编译基本系统

您必须在[.filename]##/usr/src##目录里边：

[source,shell]
....
# cd /usr/src
....

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

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

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

[source,shell]
....
# make -x -DVARIABLE target
....

这个例子里，`-_x_` 是会传递给 man:make[1] 的一个选项。查看 man:make[1] 手册有您可用的选项例子。

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

[source,shell]
....
# make -DNO_PROFILE target
....

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

[.programlisting]
....
NO_PROFILE=    true 	#    避免编译性能分析库
....

一起使用。

_目标 (target)_ 告诉 man:make[1] 什么该做。每个 [.filename]#Makefile# 定义了一定数量不同的"目标 (targets)"， 然后您选择的目标就决定了什么会发生。

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

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

[source,shell]
....
# make target
....

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

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

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

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

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

运行

[source,shell]
....
# make buildworld
....

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

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

[source,shell]
....
# make -j4 buildworld
....

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

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

==== 耗时

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

[[new-kernel]]
=== 编译和安装新内核

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

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

在 FreeBSD 中， 首先完成 <<make-buildworld,build world>> 然后再编译新内核非常重要。

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

[source,shell]
....
# cd /usr/src
# make buildkernel KERNCONF=MYKERNEL
# make installkernel KERNCONF=MYKERNEL
....

====

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

[[new-kernel-singleuser]]
=== 重启到单用户模式

您应该单用户模式测试新内核。照<<makeworld-singleuser>>处的说明去做。

[[make-installworld]]
=== 安装编译好的新系统

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

执行

[source,shell]
....
# cd /usr/src
# make installworld
....

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

举例，您执行了：

[source,shell]
....
# make -DNO_PROFILE buildworld
....

您就必须使用：

[source,shell]
....
# make -DNO_PROFILE installworld
....

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

[[post-installworld-updates]]
=== 不是由 `make installworld` 更新的更新文件

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

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

[[mergemaster]]
==== `mergemaster`

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

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

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

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

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

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

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

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

==== 手动更新

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

如果您使用 man:mergemaster[8] (作为推荐)，您可以向前跳到 <<updating-upgrading-rebooting,下一节>>。

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

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

[source,shell]
....
# cp -Rp /etc /etc.old
....

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

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

[source,shell]
....
# mkdir /var/tmp/root
# cd /usr/src/etc
# make DESTDIR=/var/tmp/root distrib-dirs distribution
....

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

[source,shell]
....
# cd /var/tmp/root
# find -d . -type d | xargs rmdir 2>/dev/null
....

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

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

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

最简单的方式是使用 man:diff[1] 去比较两个文件：

[source,shell]
....
# diff /etc/shells /var/tmp/root/etc/shells
....

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

[TIP]
.使用日戳 (Time Stamp) 命名新的 Root(根)目录([.filename]#/var/tmp/root#)，这样您可以轻松地比较两个版本的不同
====
频繁重建系统意味着必须频繁更新 [.filename]#/etc#，而这可能会有点烦琐。

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

[.procedure]
======

. 像平常一样建立系统 (Make the world)。当您想更新 [.filename]#/etc# 和其它目录里， 给目标目录一个含有当前日期的名字。假如您是 1998 年 2 月 14 日做的，您可以执行下边的：
+
[source,shell]
....
# mkdir /var/tmp/root-19980214
# cd /usr/src/etc
# make DESTDIR=/var/tmp/root-19980214 \
    distrib-dirs distribution
....
+
. 如上边列出的，从这个目录合并变化。
+ 
在您完成后，_不要_ 删除 [.filename]#/var/tmp/root-19980214# 目录。
. 在您下载了最新版的源码并改过后，执行第一步。 这样将得到一个新的目录，可能叫做 [.filename]#/var/tmp/root-19980221# (如果等了一周做的升级)。
. 您现在能看到两个目录间的不同了---在隔周的时间里使用 man:diff[1] 建立递归 diff 产生的不同：
+
[source,shell]
....
# cd /var/tmp
# diff -r root-19980214 root-19980221
....
+ 
一般情况下，这两种间的不同处比 [.filename]#/var/tmp/root-19980221/etc# 和 [.filename]#/etc# 之间的不同要小很多。 因为不同点更小，也就更容易把这些变化移到您的 [.filename]#/etc# 目录里边。
. 您现在可以删除早先的两个 [.filename]#/var/tmp/root-*# 目录：
+
[source,shell]
....
# rm -rf /var/tmp/root-19980214
....
+
. 每次您需要合并这些变化到 [.filename]#/etc# 里，就重复这个流程。
======

您可以使用 man:date[1] 自动产生目录的名称：

[source,shell]
....
# mkdir /var/tmp/root-`date "+%Y%m%d"`
....

====

[[updating-upgrading-rebooting]]
=== 重启

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

[source,shell]
....
# shutdown -r now
....

=== 结束

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

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

[source,shell]
....
# cd /usr/src/usr.bin/file
# make all install
....

[[updating-questions]]
=== 问题

==== 每个变化您都须要重建系统吗？

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

[source,shell]
....
src/games/cribbage/instr.c
src/games/sail/pl_main.c
src/release/sysinstall/config.c
src/release/sysinstall/media.c
src/shared/mk/bsd.port.mk
....

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

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

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

==== 我的编译失败，并伴随有许多 11信号 11 (或其它的数字信息) 号错误。是怎么回事呀？

这个通常表示硬件错误。 (重)建系统是个强压测试系统硬件的有效地方式， 并且常常产生内存错误。 这些正好表示它们自已做为编译器离奇地死于收到的奇怪信息。

一个确信的指示器是如果重新开始 make，并且整个过程中会死在不同的点上。

对于这种情况，您没有什么可做的，除了更换机器里的部件，看是哪一个坏了。

==== 我完成后可以删除 /usr/obj 吗？

简短地说，可以。

[.filename]#/usr/obj# 包含了所有在编译阶段生成的目标文件。通常， 在 `make buildworld` 过程中第一步之一就是删除这个目录重新开始。 这种情况下，在您完成后，保留 [.filename]#/usr/obj# 没有多大意义，还可释放一大堆磁盘空间(目前是 2 GB 左右)。

不过， 如果您很了解整个过程， 也可以让 `make buildworld` 跳过这一步。 这会让后续的联编过程执行得更快， 因为大部分的源码都不必再进行编译了。 这样做的负面效果是它可能会触发一些由于敏感的依赖关系导致的问题， 这些问题会导致联编以奇怪的方式出错并失败。 这在 FreeBSD 邮件列表里经常引起沸腾， 当有人抱怨他们 build 失败时，并没意识到这是因为自已是想抄近路。

==== 中断的 build 可以被恢复吗？

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

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

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

[source,shell]
....
… 问题修复 …
# cd /usr/src
# make -DNO_CLEAN all
....

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

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

[source,shell]
....
--------------------------------------------------------------
Building everything..
--------------------------------------------------------------
....

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

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

==== 我怎样加快建立系统的速度？

* 以单用户模式运行
* 把 [.filename]#/usr/src# 和 [.filename]#/usr/obj# 目录放到不同磁盘里的独立文件系统里。如果可能，这些磁盘在不同的磁盘控制器里。
* 更好的，是把这些文件系统放置到多个使用 man:ccd[4] (连接磁盘驱动器--concatenated disk driver)设备的磁盘里。
* 关掉 profiling (在 [.filename]#/etc/make.conf# 里设置 "NO_PROFILE=true")。您差不多用不了它。
* 在 [.filename]#/etc/make.conf# 里也为 `CFLAGS` 设置上 `-O -pipe`。 最佳优化 `-O2` 会更慢，而且 `-O` 和 `-O2` 之间的优化差别基本上可以忽略。 `-pipe` 让编译器使用管道而不用临时文件进行通信， 这样可以减少磁盘存取 (以内存作为代价)。
* 传递 `-j__n__` 选项给 man:make[1] 以便并发运行多个进程。 这样就不会考虑您的是否是单个或多个处理器机器。
* 存放 [.filename]#/usr/src# 的文件系统可以使用 `noatime` 选项来挂接 (或重新挂接)。 这样会防止文件系统记录文件的存取时间。 您可能并不需要这些信息。
+
[source,shell]
....
# mount -u -o noatime /usr/src
....
+
[WARNING]
====

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

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

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

如果在这个文件系统里 [.filename]#/usr/obj# 是很关键的，这不是问题。如果您有其它有价值的数据在同一个文件系统， 那么在您使用这个选项这前，确认备份一下。
====
+
[source,shell]
....
# mount -u -o async /usr/obj
....
+
[WARNING]
====

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

=== 如果出现了错误我该怎么办？

绝对确信您的环境没有先前 build 留下的残余。这点够简单。

[source,shell]
....
# 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-questions} 邮件列表。准备回答其它关于您的设置的问题！

[[make-delete-old]]
== 删除过时的文件、 目录和函数库

在 FreeBSD 的开发过程中， 随时可能会出现一些文件或其内容过时的情况。 这种情况有可能是由于其功能在其它地方实现了， 函数库的版本号增加， 或完全从基本系统中删去， 等等。 一般的联编和更新过程并不会删去这些旧的文件、 函数库或目录， 在更新系统之后， 应及时予以清理。 清理的好处是这些文件不会再继续占用存储 (以及备份) 空间， 另外， 如果旧的函数库或文件中存在安全或可靠性问题， 您也应更新到新的函数库， 以避免安全隐患或崩溃情形的发生。 过时的文件、 目录和函数库会列在 [.filename]#/usr/src/ObsoleteFiles.inc# 中。 接下来将介绍在系统更新过程中如何删去这些过时的文件。

我们假定您已经按照 <<canonical-build>> 介绍的步骤完成了更新操作。 在 `make installworld` 和 `mergemaster` 命令完成之后， 您应使用下面的命令检查系统中是否存在过时的文件或库：

[source,shell]
....
# cd /usr/src
# make check-old
....

如果有过时的文件， 则可以用下面的命令来删除：

[source,shell]
....
# make delete-old
....

[TIP]
====

参阅 [.filename]#/usr/src/Makefile# 可以了解其他 target 的功用。
====

在删除文件时， 系统会针对每个文件都给出提示。 您可以跳过这些提示， 并让系统自动完成删除操作， 方法是使用 make 变量 `BATCH_DELETE_OLD_FILES`， 具体做法如下：

[source,shell]
....
# make -DBATCH_DELETE_OLD_FILES delete-old
....

您也可以用 `yes` 命令和管道来达到类似的目的：

[source,shell]
....
# yes|make delete-old
....

[WARNING]
====
删去过时的文件， 有可能会破坏现有的依赖这些文件的应用程序。 对于旧的函数库来说， 这种问题出现的可能性更大。 绝大多数情况下， 您应重新联编使用旧库的所有的程序、 port 或函数库之后再执行 `make delete-old-libs`。
====

在 Ports Collection 中提供了一些检测动态连接库依赖关系的工具， 例如 package:sysutils/libchk[] 和 package:sysutils/bsdadminscripts[]。

过时的动态连接库可能会与新库冲突， 导致类似这样的警告消息：

[source,shell]
....
/usr/bin/ld: warning: libz.so.4, needed by /usr/local/lib/libtiff.so, may conflict with libz.so.5
/usr/bin/ld: warning: librpcsvc.so.4, needed by /usr/local/lib/libXext.so, may conflict with librpcsvc.so.5
....

要解决这样的问题， 需要确认安装这个库的 port：

[source,shell]
....
# pkg_info -W  /usr/local/lib/libtiff.so
/usr/local/lib/libtiff.so was installed by package tiff-3.9.4
# pkg_info -W /usr/local/lib/libXext.so
/usr/local/lib/libXext.so was installed by package libXext-1.1.1,1
....

接着卸载、 重新联编并安装 port。 您可以使用 package:ports-mgmt/portmaster[] 或 package:ports-mgmt/portupgrade[] 工具来自动完成这些操作。 在确认所有的 port 都重新联编， 并且不再使用旧库以后， 您就可以用下面的命令来删除它们了：

[source,shell]
....
# make delete-old-libs
....

[[small-lan]]
== 跟踪多台机器

如果您有多台机器想跟踪同样的源码树， 那么让它们都下载源码并重建所有东西，看起有点浪费资源： 磁盘空间、网络带宽以及 CPU 周期。 解决的办法是让一台机器处理大部分的工作，而其它的机器通过 NFS 挂接 (mount) 这些工作。这部分列举了一种这样做的方法。

[[small-lan-preliminaries]]
=== 准备

首先，确定一批机器，运行的二进制代码是同一套---我们称作 _构建集群 (build set)_。 每台机器可以使用不同的定制内核， 但它们运行的是相同的用户区二进制文件(userland binaries)。 从这批机器中选择一台机器做为 _构建机器(build machine)_。 这将是用于构建(build)系统和内核的机器。想像一下，它应该是一台快速的机器， 有足够的空余的 CPU 来执行``make buildworld``。 您也想要选一台机器做为 _测试机器(test machine)_， 这个将用于软件的更新生成产品之前对他们进行测试。这个 _必须_ 是一台您能提供的平时也可使用的机器。 它可以是"构建机器"，但没这个必要。

在这个"构建集群"里的所有机器需要从同一台机器、 同一个点上挂接 [.filename]#/usr/obj# 和 [.filename]#/usr/src#。理想地， 它们在"构建机器"上的两个不同的驱动器里， 但是在那台机器上可以进行 NFS 挂接。如果您有多个"构建集群"， [.filename]#/usr/src# 应该在某个"构建机器"上， 而在其它机器上进行 NFS 挂接。

最后，确认"构建集群"里所有机器上的 [.filename]#/etc/make.conf# 和 [.filename]#/etc/src.conf# 与"构建机器"里的相同。 这意味着"构建机器"必须构建部分基本系统用于 "构建集群"里所有机器的安装。同样， 每台"构建机器"要有它自已的内核名字，使用 [.filename]#/etc/make.conf# 里的 `KERNCONF` 进行设置，并且每台"构建机器"应该把它们列在 `KERNCONF` 里，同时把自已的内核列在最前。 "构建机器"的 [.filename]#/usr/src/sys/arch/conf# 里一定要有每台机器的内核配置文件，如果它想构建它们的内核的话。

[[small-lan-base-system]]
=== 基本系统

既然所有的妥当了，就准备构建所有的东西。如<<make-buildworld>>中描述的一样在"构建机器"上构建内核和系统， 但是什么也不安装。在构建结束后，转到"测试机器"上， 安装您刚构建的内核。如果这台机器通过 NFS 挂接了 [.filename]#/usr/src# 和 [.filename]#/usr/obj#， 在您重启到单用户模式里，您需要启动网络然后挂接他们。 最简单的方式是启动到多用户模式下，然后执行 `shutdown now` 转到单用户模式。一旦进入，您就可以安装新的内核和系统，并执行 `mergemaster`，就像平常一样。完成后， 重启返回到一般多用户模式操作这台机器。

在您确信所有在 "测试机"里都工作正常后， 就使用相同的过程在 "构建集群"里的其它机器里安装新的软件。

[[small-lan-ports]]
=== Ports

类似的想法是使用 ports 树。 第一个关键的步骤是从同一台计算机上挂接 [.filename]#/usr/ports# 到 "构建集群" 里的全部计算机。 然后正确设置 [.filename]#/etc/make.conf# 共享 distfiles。您应把 `DISTDIR` 设置到一个共享的目录里， 那里可以被任何一个 `root` 用户写入， 并且是由您的 NFS 挂接映射的。 设置每一台机器的 `WRKDIRPREFIX` 到一个本地构建 (build) 目录。最后，如果您要构建和发布包 (packages)，那么您应该设置 `PACKAGES` 到一个类似于 `DISTDIR` 的目录。
