<!--
     The FreeBSD Documentation Project
     The FreeBSD Simplified Chinese Project

     Original Revision: 1.216
     $FreeBSD$
-->

<chapter id="cutting-edge">
  <chapterinfo>
    <authorgroup>
      <author>
	<firstname>Jim</firstname>
	<surname>Mock</surname>
	<contrib>重新组织和部分更新，由</contrib>
      </author>
      <!-- Mar 2000 -->
    </authorgroup>
    <authorgroup>
      <author>
	<firstname>Jordan</firstname>
	<surname>Hubbard</surname>
	<contrib>原创：</contrib>
      </author>
      <author>
	<firstname>Poul-Henning</firstname>
	<surname>Kamp</surname>
      </author>
      <author>
	<firstname>John</firstname>
	<surname>Polstra</surname>
      </author>
      <author>
	<firstname>Nik</firstname>
	<surname>Clayton</surname>
      </author>
    </authorgroup>

    <authorgroup>
      <author>
	<firstname>张</firstname>
	<surname>雪平</surname>
	<contrib>中文翻译：</contrib>
      </author>
    </authorgroup>

    <!-- with feedback from various others -->
  </chapterinfo>

  <title>最前沿</title>

  <sect1 id="cutting-edge-synopsis">
    <title>概述</title>

    <para>&os; 在两个发行版之间都有着持续的发展。
      对于喜欢冒险的人来说，有几种机制让您的系统与最新的发展保持同步。
      警告&mdash;冒险并不适合每一个人！
      本章将有助于您决定是紧跟最新发展还是坚持发行版。</para>

    <para>读了本章后，您将了解到：</para>

    <itemizedlist>
      <listitem><para>&os.stable; 和 &os.current;两个发展分支的不同点。</para>
      </listitem>
      <listitem><para>怎样使用<application>CVSup</application>，
	  <application>CVS</application>或
	  <application>CTM</application>保持您的系统更新。</para>
      </listitem>
      <listitem><para>如何使用 <command>make buildworld</command>
	等命令来重新编译和安装基本系统。</para>
      </listitem>

    </itemizedlist>

    <para>在读本章这前，您应该了解的：</para>

    <itemizedlist>
      <listitem><para>正确设置网络连接 (<xref
      linkend="advanced-networking">)。</para>
      </listitem>
      <listitem><para>知道怎样安装附加的第三方软件(<xref
	linkend="ports">)。</para></listitem>
    </itemizedlist>
  </sect1>

  <sect1 id="current-stable">
    <title>&os.current; 和 &os.stable; 的对比</title>
    <indexterm><primary>CURRENT</primary></indexterm>
    <indexterm><primary>STABLE</primary></indexterm>

    <para>FreeBSD有两个开发分支：&os.current; 和 &os.stable;。
      这部分将对每个做些说明，并且讲述您的系统如何与各自的树一同保持最新。
      先讨论 &os.current;，然后是 &os.stable;。</para>

    <sect2 id="current">
      <title>使用最新的 &os; CURRENT</title>

      <para>我们再次强调， &os.current; 是 &os;
	开发的<quote>最前沿</quote>。&os.current;
	用户要有较高的技术能力，并且应该有能力自已解决困难的系统问题。
	如果您是个 &os; 新手，那么在安装之前最好三思。</para>

      <sect3>
	<title>&os.current; 是什么？</title>
	<indexterm><primary>快照</primary></indexterm>

	<para>&os.current; 是 &os; 的发展前沿。
	  包括了在下一个官方发行的软件中可能存在，
	  也可能不存在的发展、实验性变化、传统机制。由于许多
	  &os; 开发者每天编译 &os.current; 源代码，有时这些代码会是不能编译的。
	  虽然这些问题会很快解决，但不管是 &os.current; 引起的破坏还是急切的泛涵性
	  (greatly desired functionality) 都将是在您获取源代码的的时候值得注意的！</para>
      </sect3>

      <sect3>
	<title>谁需要 &os.current;？</title>

	<para>&os.current; 适合下边三种主要兴趣团体：</para>

	<orderedlist>
	  <listitem>
	    <para>&os; 社区的成员：
	      积极工作在源码树的某部分的人和为保持 <quote>最新</quote>
	      为绝对需求的人。</para>
	  </listitem>

	  <listitem>
	    <para>&os; 社区的成员： 为促使 &os.current;
	      保持尽可能的健全而愿花时间去解决问题的积极的测试员；
	      以及那些愿意提出关于 &os;
	      变化和总体方向的建设性建议并且提供补丁实现它们的人们。</para>
	  </listitem>

	  <listitem>
	    <para>那些只是想关注或为了参考目的使用当前 (current) 源码的人们
	      (如，为了<emphasis>阅读</emphasis>，而不是执行)。
	      这些人也偶尔做做注释或贡献代码。</para>
	  </listitem>
	</orderedlist>
      </sect3>

      <sect3>
	<title>&os.current; <emphasis>不是</emphasis>什么？</title>

	<orderedlist>
	  <listitem>
	    <para>追求最新功能， 您听说里面有一些很酷的新功能，
	      并希望成为您周围的人中第一个尝试它们的人。
	      尽管您能够因此首先了解到最新的功能，
	      但这也意味着在出现新的 bug 时您也首当其冲。</para>
	  </listitem>

	  <listitem>
	    <para>修复错漏的快捷方式。任何 &os.current
	      的既定版本在修复已知错漏的同时又可能会产生新的错漏。</para>
	  </listitem>

	  <listitem>
	    <para>无所不在的<quote>官方支持</quote>。
	      我们尽最大努力在3个<quote>合法的</quote>
	      &os.current; 组之一真诚给人们提供帮助，但是我们
	      <emphasis>没有时间</emphasis>提供技术支持。
	      这并不是因为我们是那种不喜欢帮助人解困的无耻之徒
	      (如果我们是的话，就不会制作 &os; 了)。
	      我们不能每天简单地回复上百的消息，<emphasis>而且</emphasis>
	      我们继续发展 FreeBSD！ 在改善 &os;
	      和回复大量关于实验代码的问题之间如果要做个选择的话，
	      开发人员会选择前者。</para>
	  </listitem>
	</orderedlist>
      </sect3>

      <sect3>
	<title>使用 &os.current;</title>

	<indexterm>
	  <primary>当前版</primary>
	  <secondary>使用</secondary>
	</indexterm>
	<orderedlist>
	  <listitem>
	    <para>加入 &a.current.name; 和 &a.cvsall.name; 列表。
	      这个不仅仅是个好主意，而且很 <emphasis>重要</emphasis>。如果您不去
	      <emphasis>&a.current.name;</emphasis>，
	      您就不会看到人们所做的关于系统当前状态的说明，
	      这样您就有可能在别人已经发现并解决了的一大堆问题面前难倒。
	      更重要的是您会错过一些重要的公告---对于您的系统安全可能是至关重要的。</para>

	    <para>&a.cvsall.name; 列表允许您看到每个变化的提交记录，
	      因为这些记录与其它相关信息是同步的。</para>

	    <para>要加入这些列表，或其它可能的列表，请访问
	      &a.mailman.lists.link; ，并且点击您想订阅的列项。
	      关于其它步骤的说明那里有提供。</para>
	  </listitem>

	  <listitem>
	    <para>从&os; <link
	      linkend="mirrors">镜像站点</link>获取源码。您有两种方式选择：</para>

	    <orderedlist>
              <indexterm>
                <primary><command>cvsup</command></primary>
              </indexterm>
              <indexterm>
                <primary><command>cron</command></primary>
              </indexterm>
              <indexterm>
	        <primary>当前</primary>
		<secondary>使用 <application>CVSup</application> 同步</secondary>
	      </indexterm>

	      <listitem>
		<para>与称作 <filename>standard-supfile</filename> 的
		  <filename>supfile</filename> 一起使用 <link
		  linkend="cvsup">cvsup</link>，这个可以从
		  <filename>/usr/share/examples/cvsup</filename>得到。
		  这是最被推荐的方式，因为它允许您一次获取整个集合，
		  以后就只取更改过的部分。许多人从 <command>cron</command>
		  运行 <command>cvsup</command>，以保持他们的源码自动更新。
		  您须要定制上边的 <filename>supfile</filename> 样本，并且配置 <link
		  linkend="cvsup">cvsup</link> 以适应您的环境。</para>
	      </listitem>

              <indexterm>
	        <primary>当前的</primary>
		<secondary>使用 CTM 同步</secondary>
	      </indexterm>
	      <listitem>
		<para>使用工具 <application><link
		  linkend="ctm">CTM</link></application>。
		  如果您的连接性能不太好(高价连接或只能通过电子邮件存取)，
		  <application>CTM</application> 是个选择。
		  但这也颇有争议并且常常得到到坏文件。因此很少使用它，
		  这也注定了不能长期用它来工作。对于使用 9600&nbsp;bps
		  或更快连接的人，我们推荐使用 <application><link
		  linkend="cvsup">CVSup</link></application>。
		</para>
	      </listitem>
	    </orderedlist>
	  </listitem>

	  <listitem>
	    <para>如果您获取源码是用于运行，而不只是看看，那么就获取
	      <emphasis>整个</emphasis> &os.current;，不要选部分。
	      这样做的原因是源码的大部分都依赖于其他部分，
	      要是您试着只编译其中一部分的话，保证您会陷入麻烦。</para>

	  <indexterm>
	    <primary>当前版</primary>
	    <secondary>编译</secondary>
	  </indexterm>
	    <para>在编译 &os.current; 之前，请仔细阅读
	      <filename>/usr/src</filename> 里的
	      <filename>Makefile</filename> 文件。
	      尽管是部分的升级过程，您至少也要首先<link
	      linkend="makeworld">安装新的内核和重建系统</link>。阅读
	      &a.current; 邮件列表和 <filename>/usr/src/UPDATING</filename>，
	      会让您在其它循序渐进的过程中保持最新，
	      这对于我们向下一个发行版转移是很有必要的。</para>
	  </listitem>

	  <listitem>
	    <para>热心一点！如果您正运行 &os.current;，
	      我们很想知道您关于它的一些想法，
	      尤其是关于错漏修复或增进的建议。
	      非常欢迎带有代码的建议！</para>
	  </listitem>
	</orderedlist>
      </sect3>
    </sect2>

    <sect2 id="stable">
      <title>使用最新的 &os; STABLE</title>

      <sect3>
	<title>&os.stable; 是什么?</title>
	<indexterm><primary>稳定版</primary></indexterm>

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

      <sect3>
	<title>谁需要 &os.stable;？</title>

	<para>如果您有兴趣追随 FreeBSD 的开发过程或为其做点贡献，
          尤其是和下一个
	  <quote>非计划</quote> 的 FreeBSD 发行版有关时，
	  您应该考虑采用 &os.stable;。</para>

	<para>尽管安全更新也会进入
	  &os.stable; 分支，但您并不 <emphasis>必须</emphasis> 使用
	  &os.stable; 来达到这样的目的。 每一个
	  FreeBSD 的安全公告都会解释如何修复受到影响的发行版中的问题
	  <footnote><para>这也不总是正确。我们不可能永远支持
  	    FreeBSD 的旧发行版， 尽管我们会在发布之后支持他们数年之久。
  	    关于 FreeBSD 目前对于旧发行版的支持政策的完整描述， 请参见
  	    <ulink
  	    url="&url.base;/security/">http://www.FreeBSD.org/security/</ulink>。</para>
	  </footnote>，而因为安全原因而去采用一个开发分支显然可能会同时引入一些不希望的修改。</para>

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

	<para>基于这些原因， <emphasis>不</emphasis> 推荐您盲目地追随
	  &os.stable;， 并且， 在粗略地测试过代码之前不要更新任何生产服务器到
	  &os.stable; 也非常重要。</para>

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

      <sect3>
	<title>使用&os.stable;</title>

	<indexterm>
	  <primary>稳定版</primary>
	  <secondary>使用</secondary>
	</indexterm>
	<orderedlist>
	  <listitem>
	    <para>加入 &a.stable.name; 列表。让您随时了解可能出现在
	      &os.stable; 里的<quote>build 依赖性</quote>或其它需要特别注意的问题。
	      当开发员正在考虑某些有争议的修复或更新时，
	      他们就会在这个邮件列表里发表声明，给用户机会回应，
	      看他们对于提出的变化是否还有什么问题。</para>

	    <para>&a.cvsall.name; 列表允许您看到每一个变化的提交记录条目，
	      这些变化是随着由可能的副作用引起的任何相关信息而产生的。</para>

	    <para>要加入这些列表或其他可用的，访问 &a.mailman.lists.link;
	      并点击您希望订阅的列表。关于其它步骤的说明可以在那里看到。</para>
	  </listitem>

	  <listitem>
	    <para>如果安装一个新的系统又想它尽可能稳定的话，您可以仅仅从 <ulink
	      url="ftp://snapshots.jp.FreeBSD.org/pub/FreeBSD/snapshots/"></ulink>
	      获取最新的分支快照并象其它发行版一样安装它。或者您可以从<link
	      linkend="mirrors">镜像站点</link>安装最新的 &os.stable; 发行版，
	      并按照下边的说明升级您的系统到最新的 &os.stable; 源码。</para>

	    <para>如果您已经在运行以前的 &os; 发行版，并希望通过源码方式升级，
	      那么您可能轻易的从 &os; <link linkend="mirrors">镜像站点</link>完成。
	      有两种方式：</para>

	    <orderedlist>
	      <indexterm>
		<primary><command>cvsup</command></primary>
	      </indexterm>
	      <indexterm>
		<primary><command>cron</command></primary>
	      </indexterm>
	      <indexterm>
	        <primary>稳定版</primary>
		<secondary>使用<application>CVSup</application>同步</secondary>
	      </indexterm>
	      <listitem>
		<para>与称作 <filename>standard-supfile</filename> 的
		  <filename>supfile</filename> 一起使用 <link
		  linkend="cvsup">cvsup</link>，这个可以从
		  <filename>/usr/share/examples/cvsup</filename> 得到。
		  这是最被推荐的方式，因为它允许您一次获取整个集合，
		  以后就只取更改过的部分。许多人从 <command>cron</command>
		  运行 <command>cvsup</command>，以保持他们的源码自动更新。
		  您须要定制上边的 <filename>supfile</filename> 样本，并且配置 <link
		  linkend="cvsup">cvsup</link> 以适应您的环境。</para>
	      </listitem>

	      <indexterm>
	        <primary>当前的</primary>
		<secondary>使用 CTM 同步</secondary>
	      </indexterm>
	      <listitem>
		<para>使用工具 <application><link linkend="ctm">CTM</link></application>。
		  如果您的连接性能不太好(高价连接或只能通过电子邮件存取)，
		  <application>CTM</application> 是个选择。
		  但这也颇有争议并且常常得到到坏文件。因此很少使用它，
		  这也注定了不能长期用它来工作。对于使用 9600&nbsp;bps
		  或更快连接的人，我们推荐使用 <application><link
		  linkend="cvsup">CVSup</link></application>。
		</para>
	      </listitem>
	   </orderedlist>
	 </listitem>

	  <listitem>
	    <para>本质上说，如果您需要快速存取源码并且不计较通信宽带的话，可以使用
	      <command>cvsup</command> 或 <command>ftp</command>。否则，就使用
	      <application>CTM</application>。</para>
	  </listitem>

	  <indexterm>
	    <primary>稳定版</primary>
	    <secondary>编译</secondary>
	  </indexterm>
	  <listitem>
	    <para>在编译 &os.stable; 之前，请仔细阅读
	      <filename>/usr/src</filename> 里的 <filename>Makefile</filename>。
	      您至少应该<link linkend="makeworld">安装一个新的内核并重建系统</link>，
	      首先做为升级过程的一部分。阅读 &a.stable; 邮件列表和
	      <filename>/usr/src/UPDATING</filename>，
	      可能让您在其它循序渐进的过程中保持更新，
	      这在我们向下一发行版转移时是很有必要的。</para>
	  </listitem>
	</orderedlist>
      </sect3>
    </sect2>
  </sect1>

  <sect1 id="synching">
    <title>同步您的源码</title>

    <para>有许多方式通过互联网(或电子邮件)与 &os;
      项目源码特定领域或所有领域保持更新，主要依赖于您的兴趣。
      我们提供的主要服务是<link linkend="anoncvs">匿名 CVS</link>、
      <link linkend="cvsup">CVSup</link>，和 <link linkend="ctm">CTM</link>。</para>

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

    <indexterm>
      <primary>CVS</primary>
      <secondary>匿名</secondary>
    </indexterm>

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

    <indexterm>
      <primary><application>CTM</application></primary>
    </indexterm>
    <para><application>CTM</application>，在另一方面，
      不能交互的把您有的源码与主压缩包里的进行比较，
      也不能把它们拉下来。实际上，在主 CTM 机器里，
      可以用来识别文件里自上次运行以来发生的变化的脚本，
      每天要执行好几次，任何侦测到的变化都会被压缩、
      标识上序列号并进行编码以利于能过电子邮件传输
      (只能是可打印的ASCII)。一旦接收到，
      这些<quote>CTM deltas</quote>就会被传送给 &man.ctm.rmail.1;
      工具---可以自动进行解码、校验和应用这些变化到用户的复制的源码里。
      这个过程比 <application>CVSup</application> 更为有效，
      而且更少占用我们的服务器资源，因为它不仅仅采用
      <emphasis>下拉(pull)</emphasis> 模式，还采用
      <emphasis>上推(push)</emphasis> 模式。</para>

    <para>当然，There are other trade-offs, of course.
      如果您不经意删除了您的压缩包的部分内容，
      <application>CVSup</application> 会检测到并为您重建破坏的部分。
      <application>CTM</application> 是不会这样做的，
      如果您删除了您的源码树中的某部分(并已不能恢复)，
      那么您就必须从破坏处 (从最新的CVS <quote>base delta</quote>)
      开始，使用 <application>CTM</application> 或
      <application>Anonymous CVS</application>
      进行重建，仅仅删除坏的数据并再同步。</para>
  </sect1>

  <sect1 id="makeworld">
    <title>重新编译 <quote>world</quote></title>

    <indexterm>
      <primary>重新编译 <quote>world</quote></primary>
    </indexterm>
    <para>只要您根据一定版本的 &os; (&os.stable;、&os.current; 等等)，
      已经同步了您本地的源码树，那么您就可以使用这些源码树来重建系统。</para>

    <warning>
      <title>做好备份</title>

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

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

    <warning>
      <title>订阅恰当的邮件列表</title>

      <indexterm><primary>邮件列表</primary></indexterm>
      <para>&os.stable; 和 &os.current; 分支自然是
	<emphasis>发展中的</emphasis>。为 &os;
	做贡献的都是人，偶尔也会犯错误。</para>

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

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

      <para>如可您想跟踪 &os.stable; 或 &os.current;
	而又不阅读 &a.stable; 和 &a.current; 各自的邮件列表，那么您是自找麻烦。</para>
    </warning>

    <warning>
      <title>不要使用 <command>make world</command></title>

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

    <sect2>
      <title>更新系统的规范途径</title>

      <para>要更新系统， 您必须查看
	<filename>/usr/src/UPDATING</filename> 文件， 以了解
	buildworld 之前需要完成的步骤，
	然后使用下面的过程：</para>

      <screen>&prompt.root; <userinput>make buildworld</userinput>
&prompt.root; <userinput>make buildkernel</userinput>
&prompt.root; <userinput>make installkernel</userinput>
&prompt.root; <userinput>reboot</userinput></screen>

      <note>
	<para>有些时候， 需要额外地执行一次
	  <command>mergemaster -p</command> 才能够完成
	  <maketarget>buildworld</maketarget> 步骤。
	  这些会在 <filename>UPDATING</filename> 中进行描述。
	  一般而言， 您可以简单地跳过这一步， 只要进行的不是
	  &os; 的大跨度版本升级。</para>
      </note>

      <para>在 <maketarget>installkernel</maketarget> 成功完成之后，
	您需要引导到单用户模式
	(例如， 在加载器提示后面输入 <command>boot -s</command>)。
	接下来执行：</para>

      <screen>&prompt.root; <userinput>mergemaster -p</userinput>
&prompt.root; <userinput>make installworld</userinput>
&prompt.root; <userinput>mergemaster</userinput>
&prompt.root; <userinput>reboot</userinput></screen>

      <warning>
	<title>阅读进一步的说明</title>

	<para>上边描述的序列只是有助于您开始工作的简要。
	  要清楚的理解每一不步，尤其是您想使用定制内核配置，
	  您就应阅读下面的部分。</para>
      </warning>
    </sect2>

    <sect2>
      <title>阅读 <filename>/usr/src/UPDATING</filename></title>

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

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

    <sect2>
      <title>检查 <filename>/etc/make.conf</filename></title>
      <indexterm>
        <primary><filename>make.conf</filename></primary>
      </indexterm>

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

      <para>一般的用户通常会从 <filename>/usr/share/examples/etc/make.conf</filename>
	(或者 &os;&nbsp;4.X 中的 <filename>/etc/defaults/make.conf</filename>) 复制
	<makevar>CFLAGS</makevar> 和
	<makevar>NOPROFILE</makevar> 这样的设置到
	<filename>/etc/make.conf</filename> 中并令它们生效。</para>

      <para>请考虑其他的一些选项 (例如 <makevar>COPTFLAGS</makevar>、
	<makevar>NOPORTDOCS</makevar> 等等)， 看看是否合用。</para>
    </sect2>

    <sect2>
      <title>更新 <filename>/etc</filename> 里边的文件</title>

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

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

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

      <para>最近就有个这样的例子，当时 <username>smmsp</username>
	用户是被增加了的。当 &man.mtree.8; 试着建立
	<filename>/var/spool/clientmqueue</filename> 时，安装过程失败了。</para>

      <para>解决办法是检查 <filename>/usr/src/etc/group</filename>
	并把它的组列表与您的进行比较。如果在新文件里有而您的文件里没有的，
	就把它们复制过来。同样地，您把 <filename>/etc/group</filename>
	里的任何这样的组进行更名---与 <filename>/usr/src/etc/group</filename>
	中有相同 GID，但不同名的那些。</para>

      <para>自 4.6-RELEASE 开始，您可以通过 <option>-p</option>
	选项以预建 (pre-buildworld) 模式运行 &man.mergemaster.8;。
	这样只是比较那些对于成功执行 <maketarget>buildworld</maketarget>
	或 <maketarget>installworld</maketarget> 起关键作用的文件。
	在第一次时，如果早期的 <command>mergemaster</command> 版本不支持
	<option>-p</option> 的话，就使用源码树中的新版本：</para>

      <screen>&prompt.root; <userinput>cd /usr/src/usr.sbin/mergemaster</userinput>
&prompt.root; <userinput>./mergemaster.sh -p</userinput></screen>

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

	<screen>&prompt.root; <userinput>find / -group <replaceable>GID</replaceable> -print</userinput></screen>

	<para>将显示所有 <replaceable>GID</replaceable> 组
	  (可以是组名也可以是数字地组 ID)所有的文件。</para>
      </tip>
    </sect2>

    <sect2 id="makeworld-singleuser">
      <title>改为单用户模式</title>
      <indexterm><primary>单用户 模式</primary></indexterm>

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

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

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

      <screen>&prompt.root; <userinput>shutdown now</userinput></screen>

      <para>这样就会转换到单用户模式。</para>

      <para>另外也可以，重启系统，在启动提示符处，输入
	<option>-s</option> 标识。系统就会启动单用户。再在 shell
	提示符处执行：</para>

      <screen>&prompt.root; <userinput>fsck -p</userinput>
&prompt.root; <userinput>mount -u /</userinput>
&prompt.root; <userinput>mount -a -t ufs</userinput>
&prompt.root; <userinput>swapon -a</userinput></screen>

      <para>这会检查文件系统，重新装载 <filename>/</filename>
	为读/写，参考 <filename>/etc/fstab</filename>
	装载其它所有的 UFS 文件系统，然后打开交换 (swapping) 开关。</para>


        <note>
          <para>如果您的 CMOS 时钟是设置为本地时间，而不是 GMT
	    (如果 &man.date.1; 命令输出不能显示正确的时间和地区也有确有其事)，
	    您可能也需要执行下边的命令：</para>
<screen>&prompt.root; <userinput>adjkerntz -i</userinput></screen>

          <para>这样可以确定您正确的本地时区设置&mdash;不这样做，
	    您以后可能会碰到一些问题。</para>
        </note>

    </sect2>

    <sect2>
      <title>删除 <filename>/usr/obj</filename></title>

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

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

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

      <screen>&prompt.root; <userinput>cd /usr/obj</userinput>
&prompt.root; <userinput>chflags -R noschg *</userinput>
&prompt.root; <userinput>rm -rf *</userinput></screen>
    </sect2>

    <sect2>
      <title>重编译源码</title>

      <sect3>
	<title>保存输出</title>

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

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

	<screen>&prompt.root; <userinput>script /var/tmp/mw.out</userinput>
Script started, output file is /var/tmp/mw.out
&prompt.root; <userinput>make TARGET</userinput>
<emphasis>&hellip; compile, compile, compile &hellip;</emphasis>
&prompt.root; <userinput>exit</userinput>
Script done, &hellip;</screen>

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

      <sect3 id="make-buildworld">
	<title>编译基本系统</title>

	<para>您必须在<filename>/usr/src</filename>目录里边：</para>

	<screen>&prompt.root; <userinput>cd /usr/src</userinput></screen>

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

	<para>使用 &man.make.1; 命令重建系统。这个命令会从
	  <filename>Makefile</filename> (描述组成 &os; 的程序应该怎样被重建，
	  以什么样的顺序建立等等) 里读取指令。</para>

	<para>输入的一般命令格式如下：</para>

	<screen>&prompt.root; <userinput>make -<replaceable>x</replaceable> -D<replaceable>VARIABLE</replaceable> <replaceable>target</replaceable></userinput></screen>

	<para>这个例子里，<option>-<replaceable>x</replaceable></option>
	  是会传递给 &man.make.1; 的一个选项。查看 &man.make.1;
	  手册有您可用的选项例子。</para>

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

	<screen>&prompt.root; <userinput>make -DNOPROFILE <replaceable>target</replaceable></userinput></screen>

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

	<programlisting>NOPROFILE=    true 	#    避免编译先定库</programlisting>

	<para>一起使用。</para>

	<para><replaceable>目标 (target)</replaceable> 告诉
	  &man.make.1; 什么该做。每个 <filename>Makefile</filename>
	  定义了一定数量不同的<quote>目标 (targets)</quote>，
	  然后您选择的目标就决定了什么会发生。</para>

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

	<para>大部分的时间不需要向 &man.make.1;
	  传递参数，因此您的命令看起来可能象这样：</para>

	<screen>&prompt.root; <userinput>make <replaceable>target</replaceable></userinput></screen>

	<para>从 &os; 的 2.2.5 版本开始 (实际上，是先在 &os.current;
	  分支里第一次创建，然后在 &os.stable; 的 2.2.2 和 2.2.5
	  两个版本间，进行了大翻新)，<maketarget>world</maketarget> target
	  已经分成了两个部分：<maketarget>buildworld</maketarget> 和
	  <maketarget>installworld</maketarget>。
	  从 &os; 的 5.3 版开始， <maketarget>world</maketarget> target
	  在默认时已经改为完全不动作，
	  因为它事实上对于许多用户都具有相当的危险性。</para>

	<para>正如名字所暗示的，<maketarget>buildworld</maketarget>
	  在 <filename>/usr/obj</filename> 下边建立了一个全新的树，然后
	  <maketarget>installworld</maketarget> 就在当前的机器里安装这个树。</para>

	<para>因为两个原因，这点很有用。首先，它允许您安全地完成建立
	  (build)，而没有您运行的系统组成部分的影响。建立 (build)
	  是<quote>自主的 (self hosted)</quote>。因为这样，
	  您可以安全地在以多用户模式运行的机器里执行
	  <maketarget>buildworld</maketarget> ，而不用当心不良影响。
	  但是依然推荐您在单用户模式时运行
	  <maketarget>installworld</maketarget>。</para>

	<para>第二，允许您使用 NFS 装载 (NFS mounts)
	  升级您网络里的多台计算机。如果您有三台
	  <hostid>A</hostid>、<hostid>B</hostid> 和 <hostid>C</hostid>
	  想进行升级，在<hostid>A</hostid> 执行
	  <command>make buildworld</command> 和
	  <command>make installworld</command>。然后从
	  <hostid>A</hostid> NFS 装载 (NFS mount)
	  <hostid>B</hostid> 和 <hostid>C</hostid> 的
	  <filename>/usr/src</filename> 和
	  <filename>/usr/obj</filename>，接着您就执行
	  <command>make installworld</command> 在
	  <hostid>B</hostid> 和 <hostid>C</hostid>
	  上安装建立 (build) 的结果。</para>

	<para>尽管 <maketarget>world</maketarget> target
	  仍然存在，强烈建议您不要用它。</para>

	<para>运行</para>

	<screen>&prompt.root; <userinput>make buildworld</userinput></screen>

	<para>现在可能给 <command>make</command> 指定
	  <option>-j</option> 选项了，这样会使用产生出几个并发的进程来。
	  这在多处理器 (multi-CPU) 机器里最有用。但是，
	  由于大部分的编译过程是 IO 限制远胜 CPU 限制，它在单处理器
	  (single-CPU) 的机器里也是有用的。</para>

	<para>在一般的单 CPU 机器里，您要运行：</para>

	  <screen>&prompt.root; <userinput>make -j4 buildworld</userinput></screen>

	<para>&man.make.1; 然后会有至多 4 个进程在同一时刻执行。
	  贴到邮件列表里的实验证据显示这样会收到最好的效果。</para>

	<para>如果您有一台多 CPU 机器，那您就使用 SMP
	  配置内核，试试 6 到 10 之间的值，看这些值提速如何。</para>

	<para>注意，这仍处在实验性阶段，如果提交到源码树上的话，
	  可能会断送其前程 (break this feature)。
	  如果用这个参数编译的话，您报告错误前试试不用它。</para>
      </sect3>

      <sect3>
	<title>耗时</title>
	<indexterm>
	  <primary>rebuilding <quote>world</quote></primary>
	  <secondary>timings</secondary>
	</indexterm>

	<para>有许多因素影响 build 时间，但通常一台带有
	  128&nbsp;MB 内存 500&nbsp;MHz 的
	  &pentium;&nbsp;III 要花费大约 2&nbsp; 小时来
	  build &os.stable; 树，并且在整个过程中不带什么技巧或捷径。
	  &os.current; 树花的时间还要更长一点。</para>
      </sect3>
    </sect2>

    <sect2>
      <title>编译和安装新内核</title>
      <indexterm>
        <primary>内核</primary>
	<secondary>编译</secondary>
      </indexterm>

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

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

      <para>在新的 FreeBSD 版本中，首先完成 <link
	linkend="make-buildworld">build world</link> 然后再编译新内核非常重要。</para>

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

      <screen>&prompt.root; <userinput>cd /usr/src</userinput>
&prompt.root; <userinput>make buildkernel KERNCONF=<replaceable>MYKERNEL</replaceable></userinput>
&prompt.root; <userinput>make installkernel KERNCONF=<replaceable>MYKERNEL</replaceable></userinput></screen>

	<para>在 FreeBSD&nbsp;4.2 或更早的版本里，您必须使用
	  <literal>KERNEL=</literal> 替换 <literal>KERNCONF=</literal>。在
	  2001 年 2 月 2 号以前发行的 4.2-STABLE 并不识别
	  <literal>KERNCONF=</literal>。</para>
      </note>

      <para>注意，如果您已把 <literal>内核安全级别(kern.securelevel)</literal>
	调高到了 1 以上，而且还设置了 <literal>noschg</literal>
	或相似的标识到了您的内核二进制里边，您可能会发现转换到单用户模式里使用
	<maketarget>installkernel</maketarget> 是很有必要的。
	如果您没有设置它， 则应该也能毫无问题地在多用户模式执行这两个命令。 请参考
	&man.init.8; 以了解更多关于 <literal>内核安全级(kern.securelevel)</literal>
	的信息；查看 &man.chflags.1; 了解更多关于不同文件标识的信息。</para>
      <para>如果您升级到 &os; 4.0 以前版本，您应旧的内核 build
	程序。但还是推荐您使用新版的 &man.config.8;，
	可以使用下边的命令行：</para>

      <screen>&prompt.root; <userinput>/usr/obj/usr/src/usr.sbin/config/config <replaceable>KERNELNAME</replaceable></userinput></screen>
    </sect2>

    <sect2>
      <title>重启到单用户模式</title>
      <indexterm><primary>单用户模式</primary></indexterm>

      <para>您应该单用户模式测试新内核。照<xref
	linkend="makeworld-singleuser">处的说明去做。</para>
    </sect2>

    <sect2>
      <title>安装新的系统二进制(System Binaries)</title>

      <para>如果您正建立一个足以使用 <command>make buildworld</command>
	的 &os; 版本，那么您现在应该使用
	<maketarget>installworld</maketarget> 来安装新的系统二进制。</para>

      <para>执行</para>

      <screen>&prompt.root; <userinput>cd /usr/src</userinput>
&prompt.root; <userinput>make installworld</userinput></screen>

      <note>
	<para>如果在 <command>make buildworld</command>
	  的命令行指定了变量，您就必须在
	  <command>make installworld</command> 命令行里指定同样的变量。
	  对于其它的选项也不是必需的，如，<option>-j</option>
	  就不能同 <maketarget>installworld</maketarget> 一起使用。</para>

	<para>举例，您执行了：</para>

	<screen>&prompt.root; <userinput>make -DNOPROFILE buildworld</userinput></screen>

	<para>您就必须使用：</para>

	<screen>&prompt.root; <userinput>make -DNOPROFILE installworld</userinput></screen>

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

    <sect2>
      <title>不是由 <command>make installworld</command> 更新的更新文件</title>

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

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

    <sect3 id="mergemaster">
      <sect3info>
	<authorgroup>
	  <author>
	    <firstname>Tom</firstname>
	    <surname>Rhodes</surname>
	    <contrib>贡献者：</contrib>
	  </author>
	</authorgroup>
      </sect3info>
      <title><command>mergemaster</command></title>
        <indexterm><primary><command>mergemaster</command></primary></indexterm>

      <para>&man.mergemaster.8; 工具是个有针对性的脚本 (Bourne script)，用于检测
	<filename>/etc</filename> 和 <filename>/usr/src/etc</filename>
	源码树里边的配置文件的不同点。
	这是保持系统配置文件同源码树里的一起更新的推荐方式。</para>

      <para><command>mergemaster</command> 被集成到了 3.3-RELEASE 和
	3.4-RELEASE 之间的 FreeBSD 基本系统里，这意味着自 3.3
	版本起所有的 -STABLE 和 -CURRENT 系统都有。</para>

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

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

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

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

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

      <para><quote>选择再次查看 &man.diff.1; 结果</quote>将会在提供给选择之前，
	显示文件的不同处，就象 &man.mergemaster.8; 所做的一样。</para>

      <para>在 &man.mergemaster.8; 完成了对系统文件的处理后，
	您会得到其它的选项。&man.mergemaster.8; 可能会问您是否要重建密码文件，
	如果您的 FreeBSD 版本超过 5.0，还会问您是否想要执行
	&man.MAKEDEV.8;，最后带上一个选项删除剩下的临时文件。</para>
      </sect3>

      <sect3>
	<title>手动更新</title>

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

      <para>如果您使用 &man.mergemaster.8; (作为推荐)，您可以向前跳到<link
	linkend="update-dev">下一节</link>。</para>

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

      <warning>
	<title>备份您已有的 <filename>/etc</filename></title>

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

	<screen>&prompt.root; <userinput>cp -Rp /etc /etc.old</userinput></screen>

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

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

      <screen>&prompt.root; <userinput>mkdir /var/tmp/root</userinput>
&prompt.root; <userinput>cd /usr/src/etc</userinput>
&prompt.root; <userinput>make DESTDIR=/var/tmp/root distrib-dirs distribution</userinput></screen>

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

      <screen>&prompt.root; <userinput>cd /var/tmp/root</userinput>
&prompt.root; <userinput>find -d . -type d | xargs rmdir 2&gt;/dev/null</userinput></screen>

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

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

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

      <para>最简单的方式是使用 &man.diff.1; 去比较两个文件：</para>

      <screen>&prompt.root; <userinput>diff /etc/shells /var/tmp/root/etc/shells</userinput></screen>

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

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

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

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

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

	    <screen>&prompt.root; <userinput>mkdir /var/tmp/root-19980214</userinput>
&prompt.root; <userinput>cd /usr/src/etc</userinput>
&prompt.root; <userinput>make DESTDIR=/var/tmp/root-19980214 \
    distrib-dirs distribution</userinput></screen>
	  </step>

	  <step>
	    <para>如上边列出的，从这个目录合并变化。</para>

	    <para>在您完成后，<emphasis>不要</emphasis> 删除
	      <filename>/var/tmp/root-19980214</filename> 目录。</para>
	  </step>

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

	  <step>
	    <para>您现在能看到两个目录间的不同了---在隔周的时间里使用
	      &man.diff.1; 建立递归 diff 产生的不同：</para>

	    <screen>&prompt.root; <userinput>cd /var/tmp</userinput>
&prompt.root; <userinput>diff -r root-19980214 root-19980221</userinput></screen>

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

	  <step>
	    <para>您现在可以删除早先的两个
	      <filename>/var/tmp/root-*</filename> 目录：</para>

	    <screen>&prompt.root; <userinput>rm -rf /var/tmp/root-19980214</userinput></screen>
	  </step>

	  <step>
	    <para>每次您需要合并这些变化到 <filename>/etc</filename>
	      里，就重复这个流程。</para>
	  </step>
	</procedure>

	<para>您可以使用 &man.date.1; 自动产生目录的名称：</para>

	<screen>&prompt.root; <userinput>mkdir /var/tmp/root-`date "+%Y%m%d"`</userinput></screen>
      </tip>
      </sect3>
    </sect2>

    <sect2 id="update-dev">
      <title>更新<filename>/dev</filename></title>

      <note>
        <indexterm><primary>DEVFS</primary></indexterm>
	<para>如果您正在运行 FreeBSD&nbsp;5.0 或更后的版本，
	  您可以安全地跳过这部分。这些版本使用了
	  &man.devfs.5; 来透明地分配设备结点。</para>
      </note>

      <para>许多情况下，在有必要更新设备结点时，&man.mergemaster.8;
	工具就可以实现，并且可以自动地完成。
	这里的说明用于怎样手工更新设备结点。</para>

      <para>考虑到安全，这里用的是多步流程。</para>

      <procedure>
	<step>
	  <para>复制 <filename>/var/tmp/root/dev/MAKEDEV</filename>
	    到 <filename>/dev</filename>：</para>

	  <screen>&prompt.root; <userinput>cp /var/tmp/root/dev/MAKEDEV /dev</userinput></screen>
	  <indexterm>
	    <primary><filename>MAKEDEV</filename></primary>
	  </indexterm>

	  <para>如果您使用 &man.mergemaster.8; 去更新
	    <filename>/etc</filename>，那么您的 <filename>MAKEDEV</filename>
	    脚本应该已经被更新过，虽然它不会影响检查 (使用 &man.diff.1;)，
	    必要时手工复制一下。</para>
	</step>

	<step>
	  <para>现在，给当前的 <filename>/dev</filename>
	    做个快照。照的时候一定要注意每个文件名的许可
	    (permissions)、所属(ownerships)、主从数字 (major and minor numbers)，
	    不必包括日戳 (time stamps)。最简单的方式是使用 &man.awk.1; 提取信息：</para>

	  <screen>&prompt.root; <userinput>cd /dev</userinput>
&prompt.root; <userinput>ls -l | awk '{print $1, $2, $3, $4, $5, $6, $NF}' > /var/tmp/dev.out</userinput></screen>
	</step>

	<step>
	  <para>重做设备结点：</para>

	    <screen>&prompt.root; <userinput>sh MAKEDEV all</userinput></screen>
	</step>

	<step>
	  <para>这时，把这个目录的另一个快照输出到
	    <filename>/var/tmp/dev2.out</filename>。现在检查这两个文件，
	    查找任何您没建立的设备结点。就该不会很多，但是保险一点总是好的。</para>

	  <screen>&prompt.root; <userinput>diff /var/tmp/dev.out /var/tmp/dev2.out</userinput></screen>

	  <para>您可能注意到磁盘分区 (disk slice) 的差别，它会使像：</para>

	    <screen>&prompt.root; <userinput>sh MAKEDEV sd0s1</userinput></screen>

	  <para>那样的命令重新建立分区入口 (slice entries)。您的具体情况可能不同。</para>
	</step>
      </procedure>
    </sect2>

    <sect2>
      <title>更新<filename>/stand</filename></title>

      <note>
	<para>这一步应被看作最后的扫尾工作。 您可以安全地跳过这一步。
	  如果正在使用的是 FreeBSD&nbsp;5.2 或更新的版本，
          <filename>/rescue</filename> 目录会为用户自动更新，
          其内容是在 <command>make installworld</command>
          过程中所生成的静态联编的可执行程序，
	  这就不需要更新 <filename>/stand</filename> (在 FreeBSD&nbsp;5.2
	  和更新版本中不再存在) 了。</para>
      </note>

      <para>为了完成，您可能想额外地更新 <filename>/stand</filename>
	里的文件。这些文件由链接到 <filename>/stand/sysinstall</filename>
	的二进制文件硬链接组成。这个文件要静态链接，以便没有其它文件系统时
	(尤其是 <filename>/usr</filename>) 被装载时也能工作。</para>

      <screen>&prompt.root; <userinput>cd /usr/src/release/sysinstall</userinput>
&prompt.root; <userinput>make all install</userinput></screen>
    </sect2>

    <sect2>
      <title>重启</title>

      <para>现在完成了。在您检查所有内容都放置正确后，
	您可以重启系统了。只是简单的 &man.shutdown.8; 可以这样做：</para>

      <screen>&prompt.root; <userinput>shutdown -r now</userinput></screen>
    </sect2>

    <sect2>
      <title>结束</title>

      <para>恭喜！您现在成功升级了您的 &os; 系统。</para>

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

	<screen>&prompt.root; <userinput>cd /usr/src/usr.bin/file</userinput>
&prompt.root; <userinput>make all install</userinput></screen>
    </sect2>

    <sect2>
      <title>问题</title>

      <qandaset>
	<qandaentry>
	  <question>
	    <para>每个变化您都须要重建系统吗？</para>
	  </question>

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

	    <screen><filename>src/games/cribbage/instr.c</filename>
<filename>src/games/sail/pl_main.c</filename>
<filename>src/release/sysinstall/config.c</filename>
<filename>src/release/sysinstall/media.c</filename>
<filename>src/share/mk/bsd.port.mk</filename></screen>

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

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

	    <para>当然，所有这些依赖于您想升级的频率，和您是否想跟踪
	      &os.stable; 或 &os.current;。</para>
	  </answer>
	</qandaentry>

	<qandaentry>
	  <question>
	    <para>我的编译失败，并伴随有许多 11
	      (或其它的数字信息) 号错误。是怎么回事呀？</para>
	  </question>
    <indexterm><primary>信号 11</primary></indexterm>

	  <answer>

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

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

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

	<qandaentry>
	  <question>
	    <para>我完成后可以删除 <filename>/usr/obj</filename> 吗？</para>
	  </question>

	  <answer>
	    <para>简短地说，可以。</para>

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

	    <para>只是，如果您清楚您在干什么，您可以让
	      <command>make buildworld</command> 跳过这一步。
	      这会让后继的 build 执行得更快，因为大部分的源码都不必再进行编译了。
	      这个的另一面就在于敏感的依赖问题可以潜在，
	      并以奇怪的方式引起 build 的失败。这在 &os; 邮件列表里经常引起沸腾，
	      当有人抱怨他们 build 失败时，并没意识到这是因为自已是想抄近路
	      (意思是说少了些必要的步骤)。</para>
	  </answer>
	</qandaentry>

	<qandaentry>
	  <question>
	    <para>中断的 build 可以被恢复吗？</para>
	  </question>

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

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

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

	    <screen><emphasis>&hellip; 问题修复 &hellip;</emphasis>
&prompt.root; <userinput>cd /usr/src</userinput>
&prompt.root; <userinput>make -DNOCLEAN all</userinput></screen>

	    <para>这样就不会取消先前的
	      <command>make buildworld</command> 所做的工作。</para>

	    <para>在<quote>make buildworld</quote>的输出中如果看到如下信息：</para>

	      <screen>--------------------------------------------------------------
Building everything..
--------------------------------------------------------------</screen>

	    <para>出现在 <command>make buildworld</command> 的输出中，
	      则这样做应该不会有什么问题。</para>
	    
	    <para>如果没有看到这样的信息， 或者您不确定，
	      则从头开始构建将是万无一失的做法。</para>
	  </answer>
	</qandaentry>

	<qandaentry>
	  <question>
	    <para>我怎样加快建立系统的速度？</para>
          </question>

          <answer>
	    <itemizedlist>
	      <listitem>
		<para>以单用户模式运行</para>
	      </listitem>

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

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

	      <listitem>
		<para>关掉 profiling (在 <filename>/etc/make.conf</filename>
		  里设置 <quote>NOPROFILE=true</quote>)。您差不多用不了它。</para>
	      </listitem>

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

	      <listitem>
		<para>传递 <option>-j<replaceable>n</replaceable></option> 选项给
		  &man.make.1; 以便并发运行多个进程。
		  这样就不会考虑您的是否是单个或多个处理器机器。</para>
	      </listitem>

	      <listitem><para>存放 <filename>/usr/src</filename> 的文件系统可以使用
		<option>noatime</option> 选项被装载(或再装载)。
		这样会防止文件系统记录文件的存取时间。您可能并不需要这些信息。</para>

		  <screen>&prompt.root; <userinput>mount -u -o noatime /usr/src</userinput></screen>

		  <warning>
		    <para>这个例子里假定 <filename>/usr/src</filename>
		      是在它自已的文件系统里。如果不是 (例如假设它是
		      <filename>/usr</filename> 的部分)，那么您就需要那个文件系统装载点，
		      而不是 <filename>/usr/src</filename>。</para>
		  </warning>
	      </listitem>

	      <listitem>
		<para>存放 <filename>/usr/obj</filename> 的文件系统可以使用
		  <option>async</option> 选项被装载(或被再装载)。
		  这会引起异步写盘。换句话说，写是立即完成了，
		  而数据要延迟几秒才会写到盘里。这就允许写到一堆，
		  如此产生戏剧性的性能提高。</para>

		<warning>
		  <para>记住，这个选项会使您的文件系统更加脆弱。
		    使用这个选项增大了这样一个机率：要是电源断了，
		    在机器重启后，文件系统将处在不可恢复状态。</para>

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

		<screen>&prompt.root; <userinput>mount -u -o async /usr/obj</userinput></screen>

		<warning>
		  <para>同上，如果 <filename>/usr/obj</filename>
		    不在自已的文件系统里，使用相关装载点的名字把它从例子里边替换掉。</para>
		</warning>
	      </listitem>
	    </itemizedlist>
	  </answer>
	</qandaentry>

        <qandaentry>
          <question>
            <para>如果出现了错误我该怎么办？</para>
          </question>

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

            <screen>&prompt.root; <userinput>chflags -R noschg /usr/obj/usr</userinput>
&prompt.root; <userinput>rm -rf /usr/obj/usr</userinput>
&prompt.root; <userinput>cd /usr/src</userinput>
&prompt.root; <userinput>make cleandir</userinput>
&prompt.root; <userinput>make cleandir</userinput></screen>

            <para>不错，<command>make cleandir</command> 真的要执行两次。</para>

            <para>然后重新开始整个过程，使用 <command>make buildworld</command> 开始。</para>

            <para>如果您还有问题，就把错误和 <command>uname -a</command>
	      的输出发送到 &a.questions; 邮件列表。准备回答其它关于您的设置的问题！</para>
          </answer>
        </qandaentry>
      </qandaset>
    </sect2>
  </sect1>

  <sect1 id="small-lan">
    <sect1info>
      <authorgroup>
	<author>
	  <firstname>Mike</firstname>
	  <surname>Meyer</surname>
	  <contrib>贡献者</contrib>
	</author>
      </authorgroup>
    </sect1info>
    <title>跟踪多台机器</title>
    <indexterm>
      <primary>NFS</primary>
      <secondary>安装多台机器</secondary>
    </indexterm>

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

    <sect2 id="small-lan-preliminaries">
      <title>准备</title>

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

      <para>在这个<quote>建造套件</quote>里的所有机器需要从同一台机器、
	同一个点上装载 <filename>/usr/obj</filename> 和
	<filename>/usr/src</filename>。理想地，
	它们在<quote>建造机器</quote>上的两个不同的驱动器里，
	但是在那台机器上可以进行 NFS 装载。如果您有多个<quote>建造套件</quote>，
	<filename>/usr/src</filename> 应该在某个<quote>建造机器</quote>上，
	而在其它机器上进行 NFS 装载。</para>

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

    <sect2>
      <title>基本系统</title>

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

      <para>在您确信所有在<quote>测试机器</quote>里都工作正常后，
	就使用相同的过程在<quote>建造套件</quote>里的其它机器里安装新的软件。</para>
    </sect2>

    <sect2>
      <title>Ports</title>

      <para>类似的想法是使用 ports 树。
	第一个关键的步骤是从同一台机子里装载
	<filename>/usr/ports</filename> 到<quote>建造套件</quote>里的所有机子。
	然后正确设置 <filename>/etc/make.conf</filename> 共享
	distfiles。您应把 <makevar>DISTDIR</makevar> 设置到一个共享的目录里，
	那里可以被任何一个 <username>root</username> 用户写入，并且是由您的
	NFS 装载映射的。设置每一台机子的 <makevar>WRKDIRPREFIX</makevar>
	到一个本地建造 (build) 目录。最后，如果您要建造和发布包
	(packages)，那么您应该设置 <makevar>PACKAGES</makevar>
	到一个类似于 <makevar>DISTDIR</makevar> 的目录。</para>
    </sect2>
  </sect1>
</chapter>
<!--
     Local Variables:
     mode: sgml
     sgml-declaration: "../chapter.decl"
     sgml-indent-data: t
     sgml-omittag: nil
     sgml-always-quote-attributes: t
     sgml-parent-document: ("../book.sgml" "part" "chapter")
     End:
-->
