diff --git a/documentation/content/zh-cn/articles/contributing/_index.adoc b/documentation/content/zh-cn/articles/contributing/_index.adoc index f87cdeb7e2..a7ece340a7 100644 --- a/documentation/content/zh-cn/articles/contributing/_index.adoc +++ b/documentation/content/zh-cn/articles/contributing/_index.adoc @@ -1,234 +1,246 @@ --- title: 为 FreeBSD 提供帮助 authors: - author: Jordan Hubbard releaseinfo: "$FreeBSD$" trademarks: ["freebsd", "ieee", "general"] --- = 为 FreeBSD 提供帮助 :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :toc-title: 目录 :part-signifier: 部分 :appendix-caption: 附录 :table-caption: 表 :figure-caption: 图 :example-caption: 例 +ifeval::["{backend}" == "html5"] include::shared/zh-cn/mailing-lists.adoc[] include::shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "pdf"] +include::../../../../shared/zh-cn/mailing-lists.adoc[] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "epub3"] +include::../../../../shared/zh-cn/mailing-lists.adoc[] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] [.abstract-title] 摘要 无论是作为个人还是组织机构,如果您希望为FreeBSD项目提供帮助, 都可以在本文中找到合适的方法。 ''' toc::[] 您希望给 FreeBSD 项目做点什么吗? 太好了, 我们欢迎您。 FreeBSD 正是 _依靠_ 广大用户的贡献才得以发展壮大的。 我们不仅非常感谢您所做的贡献,而且,这些工作对于 FreeBSD 的持续发展也至关重要。 也许与您想象的不同, 您既不必是一名出色的程序员, 也无须和 FreeBSD 核心团队成员有很好的私交, 我们会一视同仁的对待您的工作。 FreeBSD 的开发人员遍布全球, 大家技术专长各异, 年龄分布也非常广泛。 每天, 我们都在面对持续增长的工作而苦于没有足够的人手, 因此我们随时欢迎您的帮助。 FreeBSD 项目处理的是一个完整的操作系统环境, 而不只是一个内核或是一些零散的工具包。 因此, 我们的 [.filename]#TODO# 任务列表里包含各种各样的工作, 从文档、用户测试、演示, 到系统安装程序和高度专业的内核开发。 因此无论您的技术水平如何, 从事何种领域, 都可以帮助这个项目。 我们鼓励从事和 FreeBSD 相关工作的企业和我们联系。 您需要一些特殊的扩展来使您的产品运转起来么? 您会发现我们很乐意答应您的请求, 除非是特别稀奇古怪的。 您是否正从事相关的增值业务? 让我们来帮助您吧, 我们也许可以在其中的某些方面相互协作。 自由软件世界正在努力打破旧有的关于软件开发、 销售和维护的框框, 我们希望恳请您至少能给它一次机会。 [[contrib-what]] == 我们的需求 下面列出了一些需要完成的任务和子项目, 它们基本上可以被等同于 [.filename]#TODO#(任务列表) 列表, 以及用户的要求。 [[non-programmer-tasks]] === 正在进行中的非开发任务 很多参加FreeBSD项目的人不是程序员。 这个项目里有文档撰写者、 网页设计师、 以及技术支持人员。 对于这些志愿者来说, 他们只需要贡献一些时间, 并且具有学习的意愿。 . 您可以经常通读FAQ和手册, 如果您发现了繁琐的解释, 或者是过时的知识, 甚至完全不正确的地方, 都请告诉我们。 如果您能顺手把他们改过来那就更好了 (SGML其实并不难学, 但我们也不反对您直接提交 ASCII 的版本)。 . 帮助我们把 FreeBSD 文档翻译成您的母语。 如果您的母语版本已经存在了, 您也可以翻译一些其他的文档或者检查那些已有的文档是否是最新更新过的。 您可以先简单看看 FreeBSD 文档计划中有关 link:{fdp-primer}#translations[翻译的常见问题]。 参加翻译工作并不是说您要孤军奋战翻译所有的 FreeBSD 文档。 作为一个志愿者, 做多少工作完全取决于您的意愿。 一旦某个人开始翻译了, 其他人几乎一定会参与到这些工作中来。 如果您只有有限的时间或者精力去翻译部分文档, 您可以首先去翻译安装指南。 . 阅读 {freebsd-questions} 并偶尔看一看 link:news:comp.unix.bsd.freebsd.misc[comp.unix.bsd.freebsd.misc] (甚至有规律地这样做)。 与别人分享您的专业知识, 并帮助他们解决问题是一件令人愉悦的事情; 有些时候您甚至可以在这个过程中学到一些新东西! 这些论坛有时也会为您提供一些有价值的主意。 [[ongoing-programmer-tasks]] === 正在进行的开发任务 列在这里的大部分任务都需要您投入可观的时间, 或者需要您在 FreeBSD 内核方面有丰富的知识, 或者两者都要。 当然这里也有很多重要的任务也许您一个 "周末开发人员" 就可以干完。 . 如果您正在运行 FreeBSD-CURRENT 版本并且有一条高速的 Internet接入线路, 您可以访问 `current.FreeBSD.org`, 这里每天会有一个新版本 - 如果您有空, 您可以隔三岔五地下载一份并且安装它, 其间如果出了什么问题,请告诉我们。 . 阅读 {freebsd-bugs}。 您可能会为这些问题提供具有建设性意义的评论, 或者帮忙测试一些补丁。 此外, 您甚至可以尝试修正其中的一些问题。 . 如果您知道有一些修正已经在 -CURRENT 上成功地进行, 但在经过一段时间之后仍然没有合并到 -STABLE (通常是 2周左右), 给相关的 committer 发一封礼貌的提示信。 . 将第三方软件加入到源代码中的 [.filename]#src/contrib# 目录。 . 确保 [.filename]#src/contrib# 中的代码是最新的 . 以更高的警告级别构建源代码 (或一部分源代码) 并清理这些警告。 . 更新那些在 ports 中使用过时的东西, 例如 `gets()` 或包含 [.filename]#malloc.h# 所产生的警告。 . 如果您制作了 ports, 并进行了一些针对 FreeBSD 的改动, 将您的补丁发回给原作者 (这样下次升级时您的工作会变得轻松一些)。 . 获取一份正式的标准, 如 POSIX(R) 的副本。 您可以在 link:https://www.FreeBSD.org/projects/c99/[FreeBSD C99 & POSIX 标准顺应项目] 网站上得到相关的链接。 将 FreeBSD 的行为同标准进行比较。 如果与标准不同, 特别是那些细节地方的微小差异, 请发送一个关于它的 PR (问题报告)。 如果可能, 请指出如何修正它, 并随 PR 提交补丁。 如果您认为标准有问题, 请向标准化团体要求对其进行重新的考虑。 . 为这份列表建议更多的内容! === 查看整个 PR 数据库 http://www.FreeBSD.org/cgi/query-pr-summary.cgi[FreeBSD PR 列表] 展示了所有当前处于活跃状态的问题报告, 以及由 FreeBSD 用户提交的改进建议。 PR 数据库同时包括了开发人员和非开发人员的任务。 查看那些尚未解决的 PR, 并看看是否有您感兴趣的任务。 这其中可能有一些是非常简单的问题, 只需要看一看并确认 PR 是正确的。 另外一些可能会非常复杂, 或者完全没有包括任何修正。 首先看一看那些还没有人接手的 PR。 如果 PR 已经分配给了其它人, 但看起来是您能够处理的, 您可以给那个人发信, 并询问您是否可以提供帮助 - 他们可能已经有了可供测试的补丁, 或有一些可供讨论的意见。 === 从 "点子" 网页上认领项目 link:https://www.FreeBSD.org/projects/ideas/[FreeBSD 志愿者项目和点子清单] 也是提供给愿意为 FreeBSD 项目做出贡献的人们的。 这张清单一直在被定期更新着, 包含了对程序员和非程序员有用的每个项目的信息。 [[contrib-how]] == 如何提供帮助 帮助改进系统基本上可以分为 5 类: [[contrib-general]] === 错误报告和一般的注解 通常, _一般意义上的_ 技术想法和建议应该发到 {freebsd-hackers}。 同样地, 对于这些东西有兴趣的人 (当然, 他们同时还要能够容忍 _大量的_ 邮件!) 可以考虑订阅 {freebsd-hackers}。 参见 link:{handbook}#eresources-mail[FreeBSD 使用手册] 以了解关于这个邮件列表, 以及其它邮件列表的详细情况。 如果您发现了 bug 或者想要提交某些修改, 请通过 man:send-pr[1] 程序或使用 link:https://www.FreeBSD.org/send-pr/[基于 WEB 的提交页面] 来提交。 请试着填写 bug 报告的每一项。 一般来说, 我们建议在 bug 报告中直接附上补丁, 除非它超过了 65KB。 如果补丁可以直接应用到源代码上, 则建议您在报告的 synopsis 一栏写上 `[PATCH]`。 在附带补丁时, 请 _不要_ 通过复制和粘贴来进行, 因为这样做会把 tab 变成空格, 结果补丁很可能就不能用了。 如果补丁超过 20KB 很多, 应考虑将其压缩 (例如使用 man:gzip[1] 或 man:bzip2[1]) 之后用 man:uuencode[1] 进行编码之后再放进您的问题报告中。 一旦报告被存档, 您会收到一封确认邮件以及一个事件追踪编号。 请保留这个编号, 因为您可以在之后使用这个编号, 发邮件到 {bugfollowup} 来提供关于该事件的进一步信息。 您需要做的是将编号放到邮件的标题中, 例如 `"Re: kern/3377"`。 关于同一问题更进一步的情况应该通过这种方式来提交。 如果您在一段时间之后仍然没有收到确认信 (超过 3 天甚至 1 周, 这取决于您的邮件服务) 或者由于某种原因无法使用 man:send-pr[1] 命令, 则可以发信给 {freebsd-bugs} 要求别人代您发送它。 请参见 link:{problem-reports}[这篇文章] 了解如何撰写好的问题报告。 === 对于文档的修订 对于文档的修改由 {freebsd-doc} 来审查。 请参见 link:{fdp-primer}[FreeBSD 文档计划初级读本] 来获得完整的指导。 请按照 <> 中介绍的方法使用 man:send-pr[1] 来发送新的文档或者对于现有文档的完善 (哪怕是很小的改进也是欢迎的!)。 === 对于现有源代码的修改 在现有代码上进行修改或增加功能在某种程度上是需要更多技巧的事情, 并且还和您对于目前 FreeBSD 的开发现状的了解有关。 有多种方式可以得到被称作 "FreeBSD-CURRENT" 的 FreeBSD 开发版本, 您可以通过它来了解最近的开发情况。 请参见 link:{handbook}#current-stable[FreeBSD 使用手册] 来了解使用 FreeBSD-CURRENT 的进一步详情。 在旧的代码上进行修改, 则通常可能由于代码已经过时, 或与新的开发版本差异太大而无法被重新集成到 FreeBSD 中。 如果您订阅了 {freebsd-announce} 以及 {freebsd-current} 邮件列表, 则可以通过它们来大体了解目前的开发状态。 假如说您能够基于尽可能新的代码来完成您的修改, 则下一步要做的事情就是生成您所进行的修改的差异文件, 并将它发给 FreeBSD 的维护人员。 这项工作可以通过 man:diff[1] 命令来完成。 提交补丁时推荐的 man:diff[1] 格式是一致差异 (unified diff), 它可以通过 `diff -u` 来生成。 不过, 如果您修改了大量的代码, 则使用 `diff -c` 来生成的上下文格式 (context diff) 的差异可能更容易阅读, 因而推荐使用。 例如: [source,shell] .... % diff -c oldfile newfile .... 或者 [source,shell] .... % diff -c -r olddir newdir .... 将分别生成给定文件或目录结构的 context diff。 类似地, [source,shell] .... % diff -u oldfile newfile .... 或 [source,shell] .... % diff -u -r olddir newdir .... 的作用与前面的类似, 但采用的格式是 unified diff。 请参见 man:diff[1] 联机手册了解更多细节。 一旦您使用 man:diff[1] 生成了差异集 (可以使用 man:patch[1] 命令来测试一下), 就可以提交它们, 以便被 FreeBSD 收录。 通过使用 <> 中所介绍的 man:send-pr[1] 程序就可以完成这项工作。 _不要_ 只是把差异集发到 {freebsd-hackers}, 否则它们可能会被丢掉! 我们会非常感激您提交的修改 (这是一个志愿者项目!); 因为我们都很忙, 因此有时不一定能够立即修正问题, 但 PR 数据库将一直保持着这些记录, 因此只要有人有了时间它们就能被改正了。 如果您的问题报告中包括补丁, 一定不要忘了在标题上用 `[PATCH]` 来强调一下。 如果您认为合适 (例如您添加、 删除或重命名了文件), 还可以考虑使用 `tar` 来将文件打包, 然后用 man:uuencode[1] 来编码。 我们也欢迎用 man:shar[1] 创建的包。 如果您的修改可能存在潜在的争议, 例如, 您不确定与之相关的版权问题, 或者感觉需要经过更严格的复审才可以发布它们, 则应直接发给 {core-name}, 而不是通过 man:send-pr[1] 来发送。 {core-name} 是一个小组, 其成员更多的从事 FreeBSD 的日常工作。 需要注意的是, 这个小组也因此 __很忙__, 因此只有在非常必要的时候才应给他们写信。 请参考 man:intro[9] 和 man:style[9] 以了解关于编码习惯和约定的详情。 如果您了解这些约定, 则对我们来说将是极大的帮助。 === 新代码或重要的增值软件包 如果您打算提供规模较大的代码, 或者为 FreeBSD 增加重要的新功能, 则可能必须将它们通过 uuencode 进行编码, 或传到某个 Web 或 FTP 站点, 以便更多的人能够得到它。 如果您没有这样的服务器, 请到相关的 FreeBSD 邮件列表提出, 看看是否有人愿意帮您放置它们。 对于大量的代码而言, 关于版权的问题肯定会被提出。 FreeBSD 基本系统中能够使用的版权声明包括: . BSD 版权。 我们倾向于使用这类授权的代码, 因为它 "不附加多余的条件", 因而更能够吸引商业企业使用。 FreeBSD 并不反对商业公司使用它的代码, 相反, 我们积极地鼓励商业公司使用我们的代码, 当然, 如果它们最终把一部分代码重新捐赠给 FreeBSD 就更好了。 . GNU General Public License, 或简称 "GPL"。 我们并不很欢迎使用这样授权的代码, 因为商业公司使用它需要做更多的工作。 不过, 由于很多使用 GPL 授权的代码目前是无法避免的 (编译器、 汇编器, 文本排版程序等等), 拒绝使用所有采用这样授权的软件是很不明智的。 采用 GPL 授权的代码会被放到源代码的一些专门的位置, 例如 [.filename]#/sys/gnu# 或 [.filename]#/usr/src/gnu#, 以方便那些使用 GPL 代码可能会给他们带来问题的人识别。 使用其它授权的代码在进入 FreeBSD 之前必须经过慎重的复审和考虑。 采用包含严厉限制的商业授权的代码, 一般来说会被拒绝, 但我们鼓励这些代码的作者通过自己的渠道来发布它们。 要在您的成果上加入 "BSD式" 的版权, 请把下列文本放到每一个源文件的最开始部分, 并用适当的文字替换 `%%` 之间的文字。 [.programlisting] .... Copyright (c) %%proper_years_here%% %%your_name_here%%, %%your_state%% %%your_zip%%. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY %%your_name_here%% ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL %%your_name_here%% BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $Id$ .... 为了方便您的使用, 在 [.filename]#/usr/shared/examples/etc/bsd-style-copyright# 也可以找到此授权的副本。 === 资金、 硬件或 Internet 接入 我们非常愿意接受各种形式的捐赠, 以进一步拓展 FreeBSD Project 的事业, 因为有您的支持, 像我们这样的志愿者努力才能够有更大的成就! 捐赠硬件也非常重要, 因为这样能够帮助我们增加可以支持的硬件种类, 而我们中的很多人并没有足够的资金来购置这些硬件。 [[donations]] ==== 捐款 FreeBSD 基金会是一个非营利的、 有课税豁免权的基金会, 建立这个基金会的目标是为了让 FreeBSD Project 能够达成更加长远的目标。 作为 501(c)3 实体, 一般而言基金会可以免予上缴美国联邦收入税, 以及科罗拉多州收入税。 通常对于课税豁免的实体进行捐赠, 可以折抵联邦收入中应课税部分的金额。 您可以把支票寄往: [.address] **** The FreeBSD Foundation + P.O. Box 20247, + Boulder, + CO 80308 + USA **** FreeBSD 基金会现在可以通过 PayPal 从网上接受捐款。 如果您想向基金会捐款, 请访问它的 http://www.freebsdfoundation.org[web 站点]。 关于 FreeBSD 基金会的更多详情, 可以在 http://people.FreeBSD.org/~jdp/foundation/announcement.html[FreeBSD 基金会 -- 介绍] 找到。 要联络基金会, 请发送电子邮件到 mailto:bod@FreeBSDFoundation.org[bod@FreeBSDFoundation.org]。 ==== 捐赠硬件 FreeBSD 计划欢迎任何人捐赠可以使用的硬件。 如果您有兴趣捐赠硬件, 请联系 link:https://www.FreeBSD.org/donations/[捐赠联络人办公室]。 ==== 捐赠 Internet 接入 我们欢迎新的 FTP、 WWW 或 `cvsup` 镜像。 如果您希望成为这样的镜像, 请参见 link:{hubs}[如何架设 FreeBSD 镜像] 一文, 以了解进一步的情况。 diff --git a/documentation/content/zh-cn/articles/leap-seconds/_index.adoc b/documentation/content/zh-cn/articles/leap-seconds/_index.adoc index 2903bc3496..822030ab7d 100644 --- a/documentation/content/zh-cn/articles/leap-seconds/_index.adoc +++ b/documentation/content/zh-cn/articles/leap-seconds/_index.adoc @@ -1,78 +1,88 @@ --- title: FreeBSD 对闰秒的支持 releaseinfo: "$FreeBSD$" --- = FreeBSD 对闰秒的支持 :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :toc-title: 目录 :part-signifier: 部分 :appendix-caption: 附录 :table-caption: 表 :figure-caption: 图 :example-caption: 例 +ifeval::["{backend}" == "html5"] include::shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "pdf"] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "epub3"] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] ''' toc::[] [[leapseconds-definition]] == 介绍 __闰秒__是为了同步地球自转,而对原子钟标准时间所做的特定修正。本文描述了 FreeBSD 如何处理闰秒。 截至本文完稿时,下一个闰秒将会发生在2015年6月30日23:59:60 UTC。这个闰秒将会发生在南北美洲和亚太地区的一个工作日里。 闰秒是由 http://datacenter.iers.org/[IERS] 在 http://datacenter.iers.org/web/guest/bulletins/-/somos/5Rgv/product/16[Bulletin C] 上宣布的。 https://tools.ietf.org/html/rfc7164#section-3[RFC 7164] 描述了闰秒的标准行为。也可参见 man:time2posix[3] 。 [[leapseconds-posix]] == FreeBSD 对闰秒的默认处理方式 处理闰秒最简单的方法是使用 FreeBSD 预设的 POSIX 时间规则, 以及 link:{handbook}#network-ntp[NTP]。如果 man:ntpd[8] 正在运行,并且时间和正确处理闰秒的上游 NTP 服务器同步,闰秒将使系统时间自动重复当天的最后一秒。不需要进行其它调整。 如果上游的 NTP 服务器没有正确处理闰秒,man:ntpd[8] 会在错误的上游服务器发现错误并修正后,跟着加上一秒。 如果未使用 NTP,将需要在闰秒过后手动调整系统时钟。 [[leapseconds-cautions]] == 注意 闰秒在全世界的同一瞬间插入:UTC 午夜。日本在上午,太平洋在正午,美洲在傍晚,而欧洲在晚上。 我们相信并预期,如果提供了正确和稳定的 NTP 服务,FreeBSD 会在闰秒时按设计运作,正如在之前遇到闰秒时一样。 然而我们要警告,事实上没有应用程序会向内核询问关于闰秒的事。我们的经验是,闰秒正如设计的一样,本质上是闰秒前一秒的重播,这对大部分应用程序开发者来说是意想不到的事。 其它操作系统和电脑可能会也可能不会像 FreeBSD 一样处理闰秒,没有正确和稳定 NTP 服务的系统一点也不知道闰秒的发生。 电脑因为闰秒而崩溃并非闻所未闻,经验显示,大量的公共 NTP 服务器可能会错误处理和公告闰秒。 请试着确认不会因为闰秒而发生任何可怕的事情。 [[leapseconds-testing]] == 测试 测试是否将使用闰秒是可行的。由于 NTP 的性质,测试可能要运行到闰秒前24小时。有些主要的参考时钟来源只在闰秒事件前一小时公告。查询 NTP 守护进程: [source,shell] .... % ntpq -c 'rv 0 leap' .... 包含 `leap_add_sec` 的输出表明了对于闰秒的正确支持。`leap_none` 会在闰秒前24小时或闰秒过后显示。 [[leapseconds-conclusion]] == 结论 在实践中,FreeBSD 中的闰秒通常不是个问题。我们希望这篇文章能解释清楚这方面可能出现的状况,以及如何使闰秒事件进行得更顺利。 diff --git a/documentation/content/zh-cn/articles/linux-users/_index.adoc b/documentation/content/zh-cn/articles/linux-users/_index.adoc index faa013d6e3..f96c358fef 100644 --- a/documentation/content/zh-cn/articles/linux-users/_index.adoc +++ b/documentation/content/zh-cn/articles/linux-users/_index.adoc @@ -1,385 +1,395 @@ --- title: Linux® 用户的 FreeBSD 快速入门向导 authors: - author: John Ferrell copyright: 2008 The FreeBSD Documentation Project releaseinfo: "$FreeBSD$" trademarks: ["freebsd", "intel", "redhat", "linux", "unix", "general"] --- = Linux(R) 用户的 FreeBSD 快速入门向导 :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :toc-title: 目录 :part-signifier: 部分 :appendix-caption: 附录 :table-caption: 表 :figure-caption: 图 :example-caption: 例 +ifeval::["{backend}" == "html5"] include::shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "pdf"] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "epub3"] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] [.abstract-title] 摘要 本文档旨在快速使那些高级 Linux(R) 用户熟悉FreeBSD的一些基础知识。 ''' toc::[] [[intro]] == 简介 本文档将突出介绍 FreeBSD 与 Linux(R) 的差别, 以使得那些 Linux(R) 高级用户能自己快速熟悉 FreeBSD 的基础内容。这只是份技术上的快速入门, 并非是试图描绘这两种操作系统之间的"哲学"上的差异。 此文档假定认为你已经安装好了 FreeBSD。 如果你还没有安装 FreeBSD 或者对 FreeBSD 的安装过程方面需要帮助,请参考 FreeBSD 手册的 link:{handbook}#install[安装 FreeBSD] 一章。 [[shells]] == Shell程序:没有Bash吗? 那些从 Linux(R) 转过来的用户经常会惊讶于 Bash 不是 FreeBSD 的默认 Shell。 事实上,Bash 甚至没有包括在 FreeBSD 的默认安装中。代替的是,FreeBSD 使用 man:tcsh[1] 作为自己的默认 Shell,尽管如此,Bash 和其他你喜爱的 Shell 程序在 FreeBSD 的 link:./#SOFTWARE[Packages 和 Ports 套件] 里都可以找到。 如果你安装了其他的 Shell 你可以使用 man:chsh[1] 来设置一个用户的默认 Shell。 通常情况下, 强烈建议不要去更改 `root` 用户的默认 Shell。原因是这些 Shell 没有包括在基本系统中,正常情况下它们会被安装到 [.filename]#/usr/local/bin# 和 [.filename]#/usr/bin# 目录下。万一某天 [.filename]#/usr/local/bin# 和 [.filename]#/usr/bin# 的文件系统不能被挂载, 这样情况下 `root` 将不能进入自己默认的 Shell,从而 `root` 将不能够登录进去。 鉴于这个原因,第二个系统管理员帐户 `toor` 创建时使用的是非默认的 Shell。在安全 FAQ 可以查阅到关于 link:{faq}#TOOR-ACCOUNT[toor 帐户] 的信息。 [[software]] == Packages和Ports:在 FreeBSD 中添加软件 除了经典的 UNIX(R) 安装软件的方法 (下载源码包,解压,编辑源码,编译)外,FreeBSD 还提供了另外两种方法来安装应用程序:packages 和 ports。 你可以在 http://www.freebsd.org/ports/[这里] 到一份完整可用的 ports 和 packages 的软件清单。 [[packages]] === Packages Packages 是预编译好的应用程序,在 FreeBSD 中等价于基于 Debian/Ubuntu 的系统中的 [.filename]#.deb# 软件包以及基于 Red Hat/Fedora 的系统中的 [.filename]#.rpm# 软件包。 Packages使用 man:pkg_add[1] 来进行安装。 例如,下面的命令将用来安装 Apache 2.2: [source,shell] .... # pkg_add /tmp/apache-2.2.6_2.tbz .... 使用 `-r` 操作将告诉 man:pkg_add[1] 来自动获取并安装一个软件包,以及解决所有的依赖关系: [source,shell] .... # pkg_add -r apache22 Fetching ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-6.2-release/Latest/apache22.tbz... Done. Fetching ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-6.2-release/All/expat-2.0.0_1.tbz... Done. Fetching ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-6.2-release/All/perl-5.8.8_1.tbz... Done. [snip] To run apache www server from startup, add apache22_enable="YES" in your /etc/rc.conf. Extra options can be found in startup script. .... [NOTE] ==== 如果你正运行着 release 版本的 FreeBSD (6.2,6.3,7.0等, 通常从 CD-ROM 被安装的)`pkg_add -r` 会为其下载专门为这些特定版本构建好的软件包。 这些软件包 _可能_ 不是当前最新的程序。 你可以使用 `PACKAGESITE` 变量来覆盖默认的动作。 例如,把 `PACHAGESITE` 设置成 link:ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-6-stable/Latest/[ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-6-stable/Latest/] 来下载 6.X 系列最新的包。 ==== 想了解更多的 packages 信息请查阅 FreeBSD 手册的 4.4 小节:link:{handbook}#packages-using[使用 Packages 系统]。 [[ports]] === Ports FreeBSD 的第二种安装应用程序的方法就是使用 Ports 套件了。 Ports 套件是 FreeBSD 上的一个利用 [.filename]#Makefile# 和一些补丁文件来特定从源码定制安装各种软件程序的框架。 当安装一个 port 时系统会获取程序源码, 应用任何所需要的补丁,编译源码, 并安装应用程序(并针对依赖关系以同样的方式安装解决)。 Ports 套件,常被称作 ports 树,可以在 [.filename]#/usr/ports# 下找到。 假设Ports套件已经在安装 FreeBSD 时安装过了。 如果 Ports 套件还没有被安装可以通过 man:sysinstall[8] 来进行安装,或者使用 man:csup[1] 或 man:portsnap[8] 来从 FreeBSD 的服务器上面拉下来。在手册的 link:{handbook}#ports-using[4.5.1 小节] 可以找到安装 Ports 套件的详细介绍。 安装一个 port 就像进入 port 的目录并开始构建过程一样简单(通常情况下), 下面是从 Ports 套件安装 Apache 2.2 的例子: [source,shell] .... # cd /usr/ports/www/apache22 # make install clean .... 使用 ports 安装软件的最大好处就是能够自定义安装选项。 例如,从 ports 安装 Apache 2.2 时你可以通过设置 `WITH_LDAP` man:make[1] 变量来启用 mod_ldap: [source,shell] .... # cd /usr/ports/www/apache22 # make WITH_LDAP="YES" install clean .... 请查看 FreeBSD 手册的 4.5 小节,link:{handbook}#ports-using[使用 Ports Collection], 以获取更多关于Ports Collection 的信息。 [[which]] === Ports还是packages,我应该使用哪个? Packages 就是预编译好的 ports, 所以从源码(ports)安装与从二进制 packages 安装这两者间确实有很大关联。每种方法各有自己的优点: .Packages(二进制) * 更快速的安装 (比较大的应用程序编译起来会花很长时间)。 * 你不需要知道如何编译软件。 * 不需要在操作系统上安装编译器。 .Ports(源码) * 能够定制安装选项。 (Packages通常都是使用标准选项构建的。使用 ports 你能够定义各种各样的选项, 比如类似构建附加的模块或是更改安装路径之类的。) * 如果你喜欢的话还可以使用自己的补丁。 如果你没有一些特别的需求, packages 可能刚好最适合你的情况。如果你需要进一步定制, ports 是最适合的方法了。(请记得, 如果你需要定制而自己又更倾向于使用 packages, 你可以使用 make`package` 从 ports 构建一个定制的 package,然后复制到其他的服务器。) [[startup]] == 系统启动:运行级别在哪里? Linux(R) 使用 Sysv init 初始化系统,而 FreeBSD 使用的是传统的 BSD 风格的 man:init[8]。在 BSD 风格的 man:init[8] 中没有运行级别和 [.filename]#/etc/inittab#, 代替控制启动的是 man:rc[8] 实用程序。 [.filename]#/etc/rc# 脚本读取 [.filename]#/etc/defaults/rc.conf# 和 [.filename]#/etc/rc.conf# 文件来决定哪个服务将被启动。 特殊服务在此后由处于 [.filename]#/etc/rc.d/# 和 [.filename]#/usr/local/etc/rc.d/# 下的相应服务初始化脚本文件所启动。 这些脚本类似于位于 Linux(R) 系统中的 [.filename]#/etc/init.d/# 目录下的脚本。 **** _为何会有两个服务初始化脚本的目录呢?_ [.filename]#/etc/rc.d/# 下的脚本是属于 "基本" 系统一部分的应用程序所使用的。(man:cron[8],man:sshd[8],man:syslog[3],以及其他。) [.filename]#/usr/local/etc/rc.d/# 下的脚本是用户安装的应用程序如 Apache,Squid 等使用的。 _"基本" 系统和用户安装的应用程序之间的区别是什么?_ FreeBSD 是一套开发出来的完整的操作系统,也就是说,内核,系统类库, 还有实用应用程序(如 man:ls[1],man:cat[1],man:cp[1] 等) 全部被做为一个整体一起开发并释出。这就是被认为归属于 "基本"系统的程序。用户安装的程序并不是 "基本"系统的一部分,如 Apache,X11, Mozilla Firefox,等等。这 些用户安装的应用程序通常是使用 FreeBSD 的 Packages 和 Ports 套件安装上去的。为了将这些程序和 "基本" 系统区分开来,用户安装的应用程序通常被安装到 [.filename]#/usr/local/# 下。 因此用户安装的二进制执行文件存在于 [.filename]#/usr/local/bin# 下,配置文件在 [.filename]#/usr/local/etc# 下,以此类推。 **** 您可以通过在 [.filename]#/etc/rc.conf#(man:rc.conf[5]) 文件中增加与之对应的 `ServiceName_enable="YES"` 配置来启用服务。 看一下系统默认的 [.filename]#/etc/defaults/rc.conf# 文件, 这些默认配置可以使用 [.filename]#/etc/rc.conf# 文件来改变。 因此, 当安装附加应用程序时最好回顾下文档来决定到底该如何启用任何相关的服务。 下面的一小段内容用来在 [.filename]#/etc/rc.conf# 中启用 man:sshd[8] 和 Apache 2.2。 还指定了 Apache 应该通过 SSL 方式启动。 [.programlisting] .... # enable SSHD sshd_enable="YES" # enable Apache with SSL apache22_enable="YES" apache22_flags="-DSSL" .... 一旦服务已经在 [.filename]#/etc/rc.conf# 中启用,服务将能够从命令行启动(不需要重新启动系统): [source,shell] .... # /etc/rc.d/sshd start .... 如果服务还没有被启用,可以使用 `forcestart` 来从命令行启动: [source,shell] .... # /etc/rc.d/sshd forcestart .... [[network]] == 网络配置 [[interfaces]] === 网络接口 代替 Linux(R) 中所使用的标识网络接口所常用的 _ethX_ 格式的是,FreeBSD 使用驱动名字后跟一个数字来标识。下面 man:ifconfig[8] 的输出显示了两个 Intel(R) Pro 1000 的网络接口([.filename]#em0# 和 [.filename]#em1#): [source,shell] .... % ifconfig em0: flags=8843 mtu 1500 options=b inet 10.10.10.100 netmask 0xffffff00 broadcast 10.10.10.255 ether 00:50:56:a7:70:b2 media: Ethernet autoselect (1000baseTX ) status: active em1: flags=8843 mtu 1500 options=b inet 192.168.10.222 netmask 0xffffff00 broadcast 192.168.10.255 ether 00:50:56:a7:03:2b media: Ethernet autoselect (1000baseTX ) status: active .... [[ipaddress]] === IP配置 一个 IP 地址可以使用 man:ifconfig[8] 来指定到一个网络接口。通常,要保持重启后依然能够使用的 IP 配置信息需要包含在 [.filename]#/etc/rc.conf# 中。下列例子指定了主机名,IP 地址,以及默认网关: [.programlisting] .... hostname="server1.example.com" ifconfig_em0="inet 10.10.10.100 netmask 255.255.255.0" defaultrouter="10.10.10.1" .... 使用下面内容来为网络接口配置DHCP: [.programlisting] .... hostname="server1.example.com" ifconfig_em0="DHCP" .... [[firewall]] == 防火墙 像 Linux(R) 中的 IPTABLES 一样, FreeBSD 也提供了一个内核级的防火墙; 实际上 FreeBSD 提供了三个防火墙: * link:{handbook}#firewalls-ipfw[IPFIREWALL] * link:{handbook}#firewalls-ipf[IPFILTER] * link:{handbook}#firewalls-pf[handbook] IPFIREWALL 或是 IPFW(管理 IPFW 规则的 man:ipfw[8] 命令) 是 FreeBSD 开发者开发并维持的。 IPFW 能够与 man:dummynet[4] 配合使用来提供流量图形功能以及模拟不同网络连接类型的功能。 允许 SSH 进入的 IPFW 规则样例如下: [.programlisting] .... ipfw add allow tcp from any to me 22 in via $ext_if .... IPFILTER 是 Darren Reed 所开发的防火墙程序。不是专门针对 FreeBSD 的,它已经被移植到 NetBSD,OpenBSD,SunOS,HP/UX, 还有Solaris等一些操作系统之上。 允许 SSH 进入的 IPFILTER 命令样例如下: [.programlisting] .... pass in on $ext_if proto tcp from any to any port = 22 .... 最后一种防火墙程序,PF, 是 OpenBSD 项目所开发的。PF 是被作为 IPFILTER 的一个替代品而被创建出的。就这点而言, PF 的语法与 IPFILTER 的非常相似。 PF 可以与 man:altq[4] 配合来提供 QoS 的特性。 允许 SSH 进入的 PF 命令样例如下: [.programlisting] .... pass in on $ext_if inet proto tcp from any to ($ext_if) port 22 .... [[updates]] == 升级 FreeBSD 共有三种方法来升级 FreeBSD 系统: 源码,二进制更新,还有安装光盘。 从源码升级是最复杂的升级方法,但是提供了最棒的总体灵活性。 这个过程包含了使用 FreeBSD CVS (并行版本系统)来同步一个本地的 FreeBSD 源代码。 一旦本地源码已经更新到当前最新你便可以构建新版本的内核以及应用程序。 关于源码更新的更多信息可见于 FreeBSD 手册 link:{handbook}#cutting-edge[关于如何更新操作系统的章节]。 二进制更新类似于使用 `yum` 或 `apt-get` 更新 Linux(R) 系统。man:freebsd-update[8] 命令会获取新的更新并安装它们。 这些更新可以通过 man:cron[8] 使用程序来调度。 [NOTE] ==== 如果你使用 man:cron[8] 来预定更新, 请确信在你的 man:crontab[1] 中使用了 `freebsd-update cron` 来控制大数目的机器同时获取更新。 [.programlisting] .... 0 3 * * * root /usr/sbin/freebsd-update cron .... ==== 最后一种更新的方法,从安装光盘来升级,是个直接的过程。 从安装光盘启动并选择该选项来更新。 [[procfs]] == procfs:已是过去式但仍未被遗忘 Linux(R) 中,你可能会通过看一看 [.filename]#/proc/sys/net/ipv4/ip_forward# 来确定 IP 转发是否被启用。在 FreeBSD 中你应该使用 man:sysctl[8] 来查看这和其他方面的系统设置,在当前的 FreeBSD 版本中 man:procfs[5] 已经不赞成使用了。(虽然 `sysctl`在 FreeBSD 也同样可用。) 在 IP 转发样例中,你应该使用下列内容来确定 FreeBSD 系统中是否已经开启了 IP 转发: [source,shell] .... % sysctl net.inet.ip.forwarding net.inet.ip.forwarding: 0 .... `-a` 标志用来列出所有的系统设置: [source,shell] .... % sysctl -a kern.ostype: FreeBSD kern.osrelease: 6.2-RELEASE-p9 kern.osrevision: 199506 kern.version: FreeBSD 6.2-RELEASE-p9 0: Thu Nov 29 04:07:33 UTC 2007 root@i386-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC kern.maxvnodes: 17517 kern.maxproc: 1988 kern.maxfiles: 3976 kern.argmax: 262144 kern.securelevel: -1 kern.hostname: server1 kern.hostid: 0 kern.clockrate: { hz = 1000, tick = 1000, profhz = 666, stathz = 133 } kern.posix1version: 200112 ... .... [NOTE] ==== 某些 `sysctl` 的参数是只读的。 ==== 需要 procfs 的情况是,运行一些较老的软件,使用 man:truss[1] 来跟踪系统信号,以及 link:{handbook}#linuxemu[Linux® 二进制兼容]. (尽管,Linux(R) 二进制兼容性使用其本身的 procfs,man:linprocfs[5]。) 如果你需要挂载 procfs 你可以在 [.filename]#/etc/fstab# 中加入如下内容: [source,shell] .... proc /proc procfs rw,noauto 0 0 .... [NOTE] ==== `noauto` 会防止 [.filename]#/proc# 在启动时被自动挂载。 ==== 然后使用如下命令挂载 procfs: [source,shell] .... # mount /proc .... [[commands]] == 常用命令 [[packageCommands]] === 软件包管理 [.informaltable] [cols="1,1,1", frame="none", options="header"] |=== | Linux(R) 命令 (Red Hat/Debian) | FreeBSD 等价命令 | 目的 |`yum install package` / `apt-get install package` |`pkg_add -r package` |从远程仓库安装 _package_ |`rpm -ivh package` / `dpkg -i package` |`pkg_add -v package` |安装 package |`rpm -qa` / `dpkg -l` |`pkg_info` |列出已安装的软件包 |=== [[systemCommands]] === 系统管理 [.informaltable] [cols="1,1,1", frame="none", options="header"] |=== | Linux(R) 命令 | FreeBSD 等价命令 | 目的 |`lspci` |`pciconf` |列出 PCI 设备 |`lsmod` |`kldstat` |列出已载入的内核模块 |`modprobe` |`kldload` / `kldunload` |载入/卸载内核模块 |`strace` |`truss` |跟踪系统调用 |=== [[conclusion]] == 总结 非常希望这篇文档能够给予你足够的帮助来开始你的 FreeBSD 之路。务必要再去看一下 link:{handbook}[FreeBSD 手册] 所提到的但并没有被包含在本文档中的那些更深入广泛的主题。 diff --git a/documentation/content/zh-cn/articles/nanobsd/_index.adoc b/documentation/content/zh-cn/articles/nanobsd/_index.adoc index e58690019b..93b5b8b031 100644 --- a/documentation/content/zh-cn/articles/nanobsd/_index.adoc +++ b/documentation/content/zh-cn/articles/nanobsd/_index.adoc @@ -1,302 +1,312 @@ --- title: NanoBSD 简介 authors: - author: Daniel Gerzo copyright: 2006 The FreeBSD Documentation Project releaseinfo: "$FreeBSD$" trademarks: ["freebsd", "general"] --- = NanoBSD 简介 :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :toc-title: 目录 :part-signifier: 部分 :appendix-caption: 附录 :table-caption: 表 :figure-caption: 图 :example-caption: 例 +ifeval::["{backend}" == "html5"] include::shared/authors.adoc[] +endif::[] + +ifeval::["{backend}" == "pdf"] +include::../../../../shared/authors.adoc[] +endif::[] + +ifeval::["{backend}" == "epub3"] +include::../../../../shared/authors.adoc[] +endif::[] [.abstract-title] 摘要 这篇文档提供了关于 NanoBSD 工具的介绍信息, 这一工具可以用来创建用于嵌入式应用的 FreeBSD 系统映像, 以适应存放到袖珍闪存 (Compact Flash) 卡 (或其它大容量存储介质) 上的需要。 ''' toc::[] [[intro]] == NanoBSD 简介 NanoBSD 是 {phk-name} 目前正在开发的一项工具。 它可以用来创建用于嵌入式应用的 FreeBSD 系统映像, 以便配合袖珍闪存 (Compact Flash) 卡 (或其他大容量存储介质) 使用。 这一工具也可以用来构建定制的安装映像, 以简化通常称为 "计算设备 (computer appliances)" 的系统的安装和维护工作。 计算设备通常在产品中将捆绑硬件和软件, 或者换言之, 所有的应用程序都是预先装好的。 这些设备可以直接插到暨存的网络中, 并 (几乎是) 立即投入使用。 NanoBSD 提供的功能包括: * 可以和 FreeBSD 一样使用 Ports 和预编译包- 所有的应用程序都可以在 NanoBSD 映像中直接使用, 而方式与 FreeBSD 完全一样。 * 不减少功能 - 能够使用 FreeBSD 做的任何工作, 都可以在 NanoBSD 中使用, 除非您在创建 NanoBSD 映像时, 明确地删去它们。 * 所有对象在运行时均是只读的 - 可以安全地拔掉电源插销。 在系统非正常关闭之后, 无需运行 man:fsck[8]。 * 便于联编和定制 - 只需使用一个 shell 脚本和一个配置文件, 您可以很容易地裁减和定制适于任意需求的映像。 [[howto]] == 如何使用 NanoBSD [[design]] === NanoBSD 的设计 一旦将映像存入介质, 就可以用它来引导 NanoBSD 了。 默认情况下, 大容量存储器会划分为三个区: * 两个映像区: `code#1` 和 `code#2`。 * 一个配置文件区, 运行环境中, 可以将其挂接到 [.filename]#/cfg# 目录下。 这些分区默认情况下以只读方式挂接。 [.filename]#/etc# 和 [.filename]#/var# 目录均为 man:md[4] (malloc) 盘。 配置文件分区保存在 [.filename]#/cfg# 目录。 它包含了用于 [.filename]#/etc# 目录的文件, 在启动之后暂时以只读方式挂接。 因此, 在需要从 [.filename]#/etc# 向 [.filename]#/cfg# 目录复制所进行的、 希望在重启时保持不变的配置时, 需要进行一些额外的操作。 .在 [.filename]#/etc/resolv.conf# 中进行需要保持的修改 [example] ==== [source,shell] .... # vi /etc/resolv.conf [...] # mount /cfg # cp /etc/resolv.conf /cfg # umount /cfg .... ==== [NOTE] ==== 只有在系统启动过程中, 以及需要修改配置文件的场合, 才需要挂接包含 [.filename]#/cfg# 的那个分区。 在任何时候都保持挂接 [.filename]#/cfg# 不是一个好主意, 特别是当您把 NanoBSD 放在不适合进行大量写操作的分区时 (由于文件系统的同步进程会定期向系统盘写一些数据)。 ==== === 构建 NanoBSD 映像 NanoBSD 映像是通过使用非常简单的 [.filename]#nanobsd.sh# shell 脚本来构建的, 这个脚本可以在 [.filename]#/usr/src/tools/tools/nanobsd# 目录中找到。 这个脚本建立的映像文件, 可以用 man:dd[1] 工具复制到存储介质上。 构建 NanoBSD 映像所需的命令是: [source,shell] .... # cd /usr/src/tools/tools/nanobsd <.> # sh nanobsd.sh <.> # cd /usr/obj/nanobsd.full <.> # dd if=_.disk.full of=/dev/da0 bs=64k <.> .... <.> 进入 NanoBSD 构建脚本的主目录。 <.> 开始构建过程。 <.> 进入构建好的映像文件所在的目录。 <.> 在存储介质上安装 NanoBSD。 === 定制 NanoBSD 映像 这可能是 NanoBSD 最为重要, 同时也是您最感兴趣的功能。 同时, 您在开发 NanoBSD 应用时, 这也是相当耗时的过程。 执行下面的命令将使 [.filename]#nanobsd.sh# 从当前目录中的 [.filename]#myconf.nano# 文件读取配置: [source,shell] .... # sh nanobsd.sh -c myconf.nano .... 定制过程包含两步: * 配置选项 * 定制函数 ==== 配置选项 通过对配置进行设置, 可以配置用以传递给 NanoBSD 构建过程中 `buildworld` 和 `installworld` 阶段的联编和安装选项, 以及 NanoBSD 的主构建过程中的选项。 通过使用这些选项可以削减系统的尺寸, 使之能够放入 64MB 的存储。 您还可以进一步通过这些选项来削减 FreeBSD, 直到它只包含内核以及两三个用户环境文件为止。 配置文件中包含用以代替默认值的配置选项。 最重要的语句包括: * `NANO_NAME` - 本次构建的名称 (用于创建工作目录的名字)。 * `NANO_SRC` - 用以联编和构建映像的源码树的位置。 * `NANO_KERNEL` - 用以联编内核的配置文件的名字。 * `CONF_BUILD` - 用于传递给 `buildworld` 构建阶段的选项。 * `CONF_INSTALL` - 用于传递给 `installworld` 构建阶段的选项。 * `CONF_WORLD` - 用以传递给 `buildworld` 和 `installworld` 这两个构建阶段的选项。 * `FlashDevice` - 定义所用的介质类型。 要了解进一步的细节, 请参考 [.filename]#FlashDevice.sub# 文件。 ==== 定制函数 通过在配置文件中使用 shell 函数可以进一步微调 NanoBSD。 下面的例子展示了定制函数的基本模式: [.programlisting] .... cust_foo () ( echo "bar=baz" > \ ${NANO_WORLDDIR}/etc/foo ) customize_cmd cust_foo .... 下面是一个更贴近实际的例子, 它将默认的 [.filename]#/etc# 目录尺寸, 从 5MB 调整为 30MB: [.programlisting] .... cust_etc_size () ( cd ${NANO_WORLDDIR}/conf echo 30000 > default/etc/md_size ) customize_cmd cust_etc_size .... 除此之外, 还有几个默认的预定义定制函数: * `cust_comconsole` - 在 VGA 设备上禁止 man:getty[8] ([.filename]#/dev/ttyv*# 设备节点) 并启用串口 COM1 作为系统控制台。 * `cust_allow_ssh_root` - 允许 `root` 通过 man:sshd[8] 登录。 * `cust_install_files` - 从 [.filename]#nanobsd/Files# 目录中安装文件, 这包含一些实用的系统管理脚本。 ==== 安装预编译软件包 通过增加自定义的函数, 可以在 NanoBSD 增加预编译的软件包。 下面的函数会添加位于 [.filename]#/usr/src/tools/tools/nanobsd/packages# 的全部预编译软件包: [.programlisting] .... install_packages () ( mkdir -p ${NANO_WORLDDIR}/packages cp /usr/src/tools/tools/nanobsd/packages/* ${NANO_WORLDDIR}/packages chroot ${NANO_WORLDDIR} sh -c 'cd packages; pkg_add -v *;cd ..;' rm -rf ${NANO_WORLDDIR}/packages ) customize_cmd install_packages .... ==== 配置文件举例 下面是一个用于构建定制的 NanoBSD 映像的完整例子: [.programlisting] .... NANO_NAME=custom NANO_SRC=/usr/src NANO_KERNEL=MYKERNEL NANO_IMAGES=2 CONF_BUILD=' NO_KLDLOAD=YES NO_NETGRAPH=YES NO_PAM=YES ' CONF_INSTALL=' NO_ACPI=YES NO_BLUETOOTH=YES NO_CVS=YES NO_FORTRAN=YES NO_HTML=YES NO_LPR=YES NO_MAN=YES NO_SENDMAIL=YES NO_SHAREDOCS=YES NO_EXAMPLES=YES NO_INSTALLLIB=YES NO_CALENDAR=YES NO_MISC=YES NO_SHARE=YES ' CONF_WORLD=' NO_BIND=YES NO_MODULES=YES NO_KERBEROS=YES NO_GAMES=YES NO_RESCUE=YES NO_LOCALES=YES NO_SYSCONS=YES NO_INFO=YES ' FlashDevice SanDisk 1G cust_nobeastie() ( touch ${NANO_WORLDDIR}/boot/loader.conf echo "beastie_disable=\"YES\"" >> ${NANO_WORLDDIR}/boot/loader.conf ) customize_cmd cust_comconsole customize_cmd cust_install_files customize_cmd cust_allow_ssh_root customize_cmd cust_nobeastie .... === 更新 NanoBSD 更新 NanoBSD 相对而言较为简单: [.procedure] . 和之前一样构建新的 NanoBSD 映像文件。 . 将新的映像放入正运行的 NanoBSD 设备中的一个未用的分区。 + 与之前最初安装 NanoBSD 的步骤相比, 这一步骤最重要的区别在于这次不应使用 [.filename]#\_.disk.full# 文件 (它包含整个盘的映像), 而应安装 [.filename]#_.disk.image# 映像 (这个文件中, 只包含一个系统分区)。 . 重新启动, 并从新安装的分区中启动系统。 . 如果一切顺利的话, 升级工作就完成了。 . 如果发生了任何问题, 则可以从先前的分区启动 (其中包含了旧的、 可用的映像), 来尽可能快地恢复系统功能。 接下来可以修正新联编的版本中存在的问题, 并重复前述步骤。 要在正在运行的 NanoBSD 系统中安装新的映像, 可以使用位于 [.filename]#/root# 目录的 [.filename]#updatep1# 或 [.filename]#updatep2# 脚本, 具体使用哪一个脚本, 取决于正在运行的系统位于那个分区。 随时提供新 NanoBSD 映像所提供的服务, 以及采用的传输方法的不同, 您可以参考并使用下列三种方式之一: ==== 使用 man:ftp[1] 如果传输速度是第一要务, 采用下面的例子: [source,shell] .... # ftp myhost get _.disk.image "| sh updatep1" .... ==== 使用 man:ssh[1] 如果更倾向于安全传输, 应参考下面的例子: [source,shell] .... # ssh myhost cat _.disk.image.gz | zcat | sh updatep1 .... ==== 使用 man:nc[1] 如果远程主机既不提供 man:ftp[1] 服务, 也不提供 man:sshd[8] 服务: [.procedure] . 开始时, 在提供映像的主机上开启 TCP 监听, 并令其将映像文件发给客户机: + [source,shell] .... myhost# nc -l 2222 < _.disk.image .... + [NOTE] ==== 请确认您所使用的端口没有通过防火墙阻止来自 NanoBSD 客户机的联接请求。 ==== . 连接到提供新映像服务的主机, 并执行 [.filename]#updatep1# 脚本: + [source,shell] .... # nc myhost 2222 | sh updatep1 .... diff --git a/documentation/content/zh-cn/articles/rc-scripting/_index.adoc b/documentation/content/zh-cn/articles/rc-scripting/_index.adoc index 0ad14c6126..0cd4c5394c 100644 --- a/documentation/content/zh-cn/articles/rc-scripting/_index.adoc +++ b/documentation/content/zh-cn/articles/rc-scripting/_index.adoc @@ -1,601 +1,611 @@ --- title: BSD rc.d脚本编程实战 authors: - author: Yar Tikhiy email: yar@FreeBSD.org copyright: 2005-2006, 2012 The FreeBSD Project releaseinfo: "$FreeBSD$" trademarks: ["freebsd", "netbsd", "general"] --- = BSD rc.d脚本编程实战 :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :toc-title: 目录 :part-signifier: 部分 :appendix-caption: 附录 :table-caption: 表 :figure-caption: 图 :example-caption: 例 +ifeval::["{backend}" == "html5"] include::shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "pdf"] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "epub3"] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] [.abstract-title] 摘要 初学者可能会发现,难以通过正式的文档, 基于 BSD 的 [.filename]#rc.d# 框架,编写一些实际任务的 [.filename]#rc.d# 脚本。 本文中,我们采用了一些复杂性不断增加的典型案例, 来展示适合每个案例的 [.filename]#rc.d# 特性, 并探讨其中的工作原理。 这样的实验为大家进一步研究设计有效的 [.filename]#rc.d# 应用程序提供了一些参考点。 ''' toc::[] [[rcng-intro]] == 简介 历史上 BSD 曾有过一个单一的启动脚本, [.filename]#/etc/rc#。 该脚本在系统启动的时候被 man:init[8] 程序所引导,并执行所有多用户操作所需求的用户级任务: 检查并挂载文件系统,设置网络,启动守护进程,等等。 在每个系统中实际的任务清单也并不相同; 管理员需要根据需求自定义这样的任务清单。在一些特殊的情况中, 还不得不去修改 [.filename]#/etc/rc# 文件, 一些真正的黑客乐此不疲。 单一脚本启动方法的真正问题是它没有提供对从 [.filename]#/etc/rc# 启动的单个组件的控制。 拿一个例子来说吧,[.filename]#/etc/rc# 不能够重新启动某个单独的守护进程。 系统管理员不得不手动找出守护进程,并杀掉它, 等待它真正退出后,再通过浏览 [.filename]#/etc/rc# 得到该守护进程的标识,最终输入全部命令来再次启动守护进程。 如果重新启动的服务包括不止一个守护进程或需要更多动作的话, 该任务将变得更加困难以及容易出错。简而言之, 单一脚本在实现我们这样的目的上是不成功的: 让系统管理员的生活更轻松。 再后来,为了将最重要的一些子系统独立出来, 便尝试将部分的内容从 [.filename]#/etc/rc# 分离出来了。 最广为人知的例子就是用来启动联网的 [.filename]#/etc/netstart# 文件。它容许从单用户模式访问网络, 但由于它的部分代码需要和一些与联网完全无关的动作交互, 所以它并没有完美地结合到自启动的进程中。那便是为何 [.filename]#/etc/netstart# 被演变成 [.filename]#/etc/rc.network# 的原因了。 后者不再是一个普通的脚本;它包括了庞大的,由 [.filename]#/etc/rc# 在不同的系统启动级别中调用的凌乱的 man:sh[1] 函数。然而,当启动任务变得多样化以及久经更改, "类模块化" 方法变得比曾经的整体 [.filename]#/etc/rc# 更缓慢费事。 由于没有一个干净和易于设计的框架, 启动脚本不得不全力更改以满足飞速开发中基于 BSD 的操作系统的需求。 它逐渐变得明朗并经过许多必要的步骤最终变成一个具有细密性和扩展性的 [.filename]#rc# 系统。BSD [.filename]#rc.d# 就这样诞生了。Luke Mewburn 和 NetBSD 社区是公认的 [.filename]#rc.d# 之父。再之后它被引入到了 FreeBSD 中。 它的名字引用为系统单独的服务脚本的位置,也就是 [.filename]#/etc/rc.d# 下面的那些脚本。 之后我们将学习到更多的 [.filename]#rc.d# 系统的组件并看看单个脚本是如何被调用的。 BSD [.filename]#rc.d# 背后的基本理念是 _良好_ 的模块化和代码重用性。 _良好_ 的模块化意味着每个基本 "服务" 就象系统守护进程或原始启动任务那样, 通过属于它们的可启动该服务的 man:sh[1] 脚本,来停止服务, 重载服务,检查服务的状态。具体动作由脚本的命令行参数所决定。 [.filename]#/etc/rc# 脚本仍然掌管着系统的启动, 但现在它仅仅是使用 `start` 参数来一个个调用那些小的脚本。 这便于用 `stop` 来对运行中的同样的脚本很好地执行停止任务, 这是被 [.filename]#/etc/rc.shutdown# 脚本所完成的。看,这是多么好地体现了 Unix 的哲学: 拥有一组小的专用的工具,每个工具尽可能好地完成自己的任务。 _代码重用_ 意味着所有的通用操作由 [.filename]#/etc/rc.subr# 中的一些 man:sh[1] 函数所实现。 现在一个典型的脚本只需要寥寥几行的 man:sh[1] 代码。最终,man:rcorder[8] 成为了 [.filename]#rc.d# 框架中重要的一部分, 它用来帮助 [.filename]#/etc/rc# 处理小脚本之间的依赖关系并有次序地运行它们。它同样帮助 [.filename]#/etc/rc.shutdown# 做类似的事情, 因为正确的关闭次序是相对于启动的次序的。 BSD [.filename]#rc.d# 的设计在 <> 中有记录, 以及 [.filename]#rc.d# 组件也被充分详细地记录在各自的 <> 中。然而, 它可能没能清晰展现给一个 [.filename]#rc.d# 新手,如何将无数的块和片进行关联来为具体的任务创建一个好风格的脚本。 因此本文将试着以不同的方式来讲述 [.filename]#rc.d#。 它将展示在某些典型情况中应该使用哪些特性,并阐述了为何如此。 注意这并不是一篇 how-to 文档,我们的目的不是给出现成的配方, 而是在展示一些简单的进入 [.filename]#rc.d# 的范围的门路。 本文也不是相关联机手册的替代品。 阅读本文时记得同时参考联机手册以获取更完整正规的文档。 理解本文需要一些先决条件。首先,你需要熟悉 man:sh[1] 脚本编程语言以掌握 [.filename]#rc.d#, 还有,你需要知道系统是如何执行用户级的启动和停止任务,这些在 man:rc[8] 中都有说明。 本文关注的是 [.filename]#rc.d# 的 FreeBSD 分支。 不过,它可能对 NetBSD 的开发者也同样有用,因为 BSD [.filename]#rc.d# 的两个分支不只是共享了同样的设计, 还保留了对脚本编写者都可见的类似观点。 [[rcng-task]] == 任务描述 在开始打开 `$EDITOR`(编辑器) 之前进行小小的思考不是坏事。为了给一个系统服务写一个 "听话的"[.filename]#rc.d# 脚本, 我们首先应该能回答以下问题: * 该服务是必须性的还是可选性的? * 脚本将为单个程序服务,如一个守护进程,还是执行更复杂的动作? * 我们的服务依赖哪些服务?反过来哪些服务依赖我们的服务? 从下面的例子中我们将看到,为什么说知道这些问题的答案是很重要的。 [[rcng-dummy]] == 虚拟的脚本 下面的脚本是用来在每次系统启动时发出一个信息: [.programlisting] .... #!/bin/sh <.> . /etc/rc.subr <.> name="dummy" <.> start_cmd="${name}_start" <.> stop_cmd=":" <.> dummy_start() <.> { echo "Nothing started." } load_rc_config $name <.> run_rc_command "$1" <.> .... 需要注意的是: ➊ 一个解释性的脚本应该以一行魔幻的 "shebang" 行开头。 该行指定了脚本的解析程序。由于 shebang 行的作用, 假如再有可执行位的设置, 脚本就能象一个二进制程序一样被精确地调用执行。 (请参考 man:chmod[1]。) 例如, 一个系统管理员可以从命令行手动运行我们的脚本: [source,shell] .... # /etc/rc.d/dummy start .... [NOTE] ==== 为了使 [.filename]#rc.d# 框架正确地管理脚本, 它的脚本需要用 man:sh[1] 语言编写。 如果你的某个服务或 port 套件使用了二进制控制程序或是用其它语言编写的例程, 请将其组件安装到 [.filename]#/usr/sbin#(相对于系统) 或 [.filename]#/usr/local/sbin#(相对于ports), 然后从合适的 [.filename]#rc.d# 目录的某个 man:sh[1] 脚本调用它。 ==== [TIP] ==== 如果你想知道为什么 [.filename]#rc.d# 脚本必须用 man:sh[1] 语言编写的细节,先看下 [.filename]#/etc/rc# 是如何依靠 `run_rc_script` 调用它们, 然后再去学习 [.filename]#/etc/rc.subr# 下 `run_rc_script` 的相关实现。 ==== ➋ 在 [.filename]#/etc/rc.subr# 下, 有许多定义过的 man:sh[1] 函数可供每个 [.filename]#rc.d# 脚本使用。这些函数在 man:rc.subr[8] 中都有说明。尽管理论上可以完全不使用 man:rc.subr[8] 来编写一个 [.filename]#rc.d# 脚本,但它的函数已经证明了它真的很方便, 并且能使任务更加的简单。所以所有人在编写 [.filename]#rc.d# 脚本时都会求助于 man:rc.subr[8] 也不足为奇了。当然我们也不例外。 一个 [.filename]#rc.d# 脚本在其调用 man:rc.subr[8] 函数之前必须先 "source"[.filename]#/etc/rc.subr#(用 "`.`"将其包含进去), 而使 man:sh[1] 程序有机会来获悉那些函数。 首选风格是在脚本的最开始 source [.filename]#/etc/rc.subr# 文件。 [NOTE] ==== 某些有用的与联网有关的函数由另一个被包含进来的文件提供, [.filename]#/etc/network.subr# 文件。 ==== ➌ [[name-var]]强制的变量 `name` 指定我们脚本的名字。 这是 man:rc.subr[8] 所强调的。也就是, 每个 [.filename]#rc.d# 脚本在调用 man:rc.subr[8] 的函数之前必须设置 `name` 变量。 现在是时候来为我们的脚本一次性选择一个独一无二的名字了。 在编写这个脚本的时我们将在许多地方用到它。在开始之前, 我们来给脚本文件也取个相同的名字。 [NOTE] ==== 当前的 [.filename]#rc.d# 脚本风格是把值放在双引号中来给变量赋值。 请记住这只是个风格问题,可能并不总是这样。 你可以在只是简单的并不包括 man:sh[1] 元字符的词句中放心地省略掉引号, 而在某些情况下你将需要使用单引号以防止 man:sh[1] 对任何的变量的解释。 程序员是可以灵巧地由风格惯例获悉其语法以及使用的。 ==== ➍ man:rc.subr[8] 背后主要的构思是 [.filename]#rc.d# 脚本提供处理程序,或者方法,来让 man:rc.subr[8] 调用。特别是,`start`, `stop`,以及其它的 [.filename]#rc.d# 脚本参数都是这样被处理的。方法是存储在一个以 `_argument_cmd_` 形式命名的变量中的 man:sh[1] 表达式,该 _argument_ 对应着脚本命令行中所特别指定的参数。我们稍后将看到 man:rc.subr[8] 是如何为标准参数提供默认方法的。 [NOTE] ==== 为了让 [.filename]#rc.d# 中的代码更加统一, 常见的是在任何适合的地方都使用 `${name}` 形式。 这样一来,可以轻松地将一些代码从一个脚本拷贝到另一个中使用。 ==== ➎ 我们应谨记 man:rc.subr[8] 为标准参数提供了默认的方法。 因此,如果希望它什么都不做的话,我们必须使用无操作的 man:sh[1] 表达式来改写标准的方法。 ➏ 比较复杂的方法主体可以用函数来实现。 在能够保证函数名有意义的情况下,这是个很不错的想法。 [IMPORTANT] ==== 强烈推荐给我们脚本中所定义的所有函数名都添加类似 `${name}` 这样的前缀,以使它们永远不会和 man:rc.subr[8] 或其它公用包含文件中的函数冲突。 ==== ➐ 这是在请求 man:rc.subr[8] 载入 man:rc.conf[5] 变量。 尽管我们这个脚本中使用的变量并没有被其它地方使用,但由于 man:rc.subr[8] 自身所控制着的 man:rc.conf[5] 变量存在的原因,仍然推荐脚本去装载 man:rc.conf[5]。 ➑ 通常这是 [.filename]#rc.d# 脚本的最后一个命令。 它调用 man:rc.subr[8] 体系使用我们脚本所提供的变量和方法来执行相应的请求动作。 [[rcng-confdummy]] == 可配置的虚拟脚本 现在我们来给我们的虚拟脚本增加一些控制参数吧。正如你所知, [.filename]#rc.d# 脚本是由 man:rc.conf[5] 所控制的。 幸运的是,man:rc.subr[8] 隐藏了所有复杂化的东西。 下面这个脚本使用 man:rc.conf[5] 通过 man:rc.subr[8] 来查看它是否在第一个地方被启用,并获取一条信息在启动时显示。 事实上这两个任务是相互独立的。一方面,[.filename]#rc.d# 脚本要能够支持启动和禁用它的服务。另一方面, [.filename]#rc.d# 脚本必须能具备配置信息变量。 我们将通过下面同一脚本来演示这两方面的内容: [.programlisting] .... #!/bin/sh . /etc/rc.subr name=dummy rcvar=dummy_enable <.> start_cmd="${name}_start" stop_cmd=":" load_rc_config $name <.> eval "${rcvar}=\${${rcvar}:-'NO'}" <.> dummy_msg=${dummy_msg:-"Nothing started."} <.> dummy_start() { echo "$dummy_msg" <.> } run_rc_command "$1" .... 在这个样例中改变了什么? ➊ 变量 `rcvar` 指定了 ON/OFF 开关变量的名字。 ➋ 现在 `load_rc_config` 在任何 man:rc.conf[5] 变量被访问之前就在脚本中被预先调用。 [NOTE] ==== 检查 [.filename]#rc.d# 脚本时,切记 man:sh[1] 会把函数延迟到其被调用时才对其中的表达式进行求值运算。 因此尽可能晚地在 `run_rc_command` 之前调用 `load_rc_config`, 以及仍然访问从方法函数输出到 `run_rc_command` 的 man:rc.conf[5] 变量并不是一个错误。这是因为方法函数将在 `load_rc_config` _之后_, 被调用的 `run_rc_command` 调用。 ==== ➌ 如果自身设置了 `rcvar`, 但指示开关变量却未被设置,那么 `run_rc_command` 将发出一个警告。如果你的 [.filename]#rc.d# 脚本是为基本系统所用的,你应当在 [.filename]#/etc/defaults/rc.conf# 中给开关变量添加一个默认的设置并将其标注在 man:rc.conf[5] 中。 否则的话你的脚本应该给开关变量提供一个默认设置。 范例中演示了一个可移植接近于后者情况的案例。 [NOTE] ==== 你可以通过将开关变量设置为 ON 来使 man:rc.subr[8] 有效, 使用 `one` 或 `force` 为脚本的参数加前缀,如 `onestart` 或 `forcestop` 这样,会忽略其当前的设置。 切记 `force` 在我们下面要提到的情况下有额外的危险后果,那就是当用 `one` 改写了 ON/OFF 开关变量。例如, 假定 `dummy_enable` 是 `OFF` 的,而下面的命令将忽略系统设置而强行运行 `start` 方法: [source,shell] .... # /etc/rc.d/dummy onestart .... ==== ➍ 现在启动时显示的信息不再是硬编码在脚本中的了。 它是由一个命名为 `dummy_msg` 的 man:rc.conf[5] 变量所指定的。这就是 man:rc.conf[5] 变量如何来控制 [.filename]#rc.d# 脚本的一个小例子。 [IMPORTANT] ==== 我们的脚本所独占使用的所有 man:rc.conf[5] 变量名, 都必须具有同样的前缀:`${name}`。 例如:`dummy_mode`, `dummy_state_file`,等等。 ==== [NOTE] ==== 当可以内部使用一个简短的名字时,如 `msg` 这样,为我们的脚本所引进的全部的共用名添加唯一的前缀 `${name}`,能够避免我们与 man:rc.subr[8] 命名空间冲突的可能。 只要一个 man:rc.conf[5] 变量与其内部等同值是相同的, 我们就能够使用一个更加兼容的表达式来设置默认值: [.programlisting] .... : ${dummy_msg:="Nothing started."} .... 尽管目前的风格是使用了更详细的形式。 通常,基本系统的 [.filename]#rc.d# 脚本不需要为它们的 man:rc.conf[5] 变量提供默认值, 因为默认值应该是在 [.filename]#/etc/defaults/rc.conf# 设置过了。但另一方面,为 ports 所用的 [.filename]#rc.d# 脚本应提供如范例所示的默认设置。 ==== ➎ 这里我们使用 `dummy_msg` 来实际地控制我们的脚本,即,发一个变量信息。 [[rcng-daemon]] == 启动并停止简单守护进程 我们早先说过 man:rc.subr[8] 是能够提供默认方法的。 显然,这些默认方法并不是太通用的。 它们都是适用于大多数情况下来启动和停止一个简单的守护进程况。 我们来假设现在需要为一个叫做 `mumbled` 的守护进程编写一个 [.filename]#rc.d# 脚本, 在这里: [.programlisting] .... #!/bin/sh . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" <.> load_rc_config $name run_rc_command "$1" .... 感到很简单吧,不是么?我们来检查下我们这个小脚本。 只需要注意下面的这些新知识点: ➊ 这个 `command` 变量对于 man:rc.subr[8] 来说是有意义的。当它被设置时, man:rc.subr[8] 将根据提供传统守护进程的情形而生效。 特别是,将为这些参数提供默认的方法: `start`,`stop`, `restart`,`poll`, 以及 `status`。 该守护进程将会由运行中的 `$command` 配合由 `$mumbled_flags` 所指定的命令行标帜来启动。 因此,对默认的 `start` 方法来说, 所有的输入数据在我们脚本变量集合中都可用。与 `start` 不同的是, 其他方法可能需要与进程启动相关的额外信息。举个例子, `stop` 必须知道进程的 PID 号来终结进程。 在目前的情况中,man:rc.subr[8] 将扫描全部进程的清单, 查找一个名字等同于 `$procname` 的进程。 后者是另一个对 man:rc.subr[8] 有意义的变量, 并且默认它的值跟 `command` 一样。 换而言之,当我们给 `command` 设置值后, `procname` 实际上也设置了同样的值。 这启动我们的脚本来杀死守护进程并检查它是否正在第一个位置运行。 [NOTE] ==== 某些程序实际上是可执行的脚本。 系统启动脚本的解释器以传递脚本名为命令行参数的形式来运行脚本。 然后被映射到进程列表中,这会使 man:rc.subr[8] 迷惑。因此,当 `$command` 是一个脚本的时,你应该额外地设置 `command_interpreter` 来让 man:rc.subr[8] 知晓进程的实际名字。 对每个 [.filename]#rc.d# 脚本而言, 有一个可选的 man:rc.conf[5] 变量给 `command` 指示其优先级。 它的名字是下面这样的形式:`${name}_program`, `name` 是我们 <> 讨论过的必须性变量。如,在这个案例中它应该命名为 `emumbled_program`。这其实是 man:rc.subr[8] 分配 `${name}_program` 来改写 `command` 的。 当然,即使 `command` 未被设置, man:sh[1] 也将允许你从 man:rc.conf[5] 或自身来设置 `${name}_program`。在那种情况下, `${name}_program` 的特定属性丢失了, 并且它成为了一个能供你的脚本用于其自身目的的普通变量。 然而,单独使用 `${name}_program` 是并不是我们所寄望的,因为同时使用它和 `command` 已成为了 [.filename]#rc.d# 脚本编程的一个惯用的约定。 ==== 关于默认方法的更详细的信息,请参考 man:rc.subr[8]。 [[rcng-daemon-adv]] == 启动并停止高级守护进程 我们来给之前的 "骨架" 脚本加点 "血肉",并让它更复杂更富有特性吧。 默认的方法已能够为我们做很好的工作了, 但是我们可能会需要它们一些方面的调整。 现在我们将学习如何调整默认方法来符合我们的需要。 [.programlisting] .... #!/bin/sh . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" command_args="mock arguments > /dev/null 2>&1" <.> pidfile="/var/run/${name}.pid" <.> required_files="/etc/${name}.conf /usr/shared/misc/${name}.rules" <.> sig_reload="USR1" <.> start_precmd="${name}_prestart" <.> stop_postcmd="echo Bye-bye" <.> extra_commands="reload plugh xyzzy" <.> plugh_cmd="mumbled_plugh" <.> xyzzy_cmd="echo 'Nothing happens.'" mumbled_prestart() { if checkyesno mumbled_smart; then <.> rc_flags="-o smart ${rc_flags}" <.> fi case "$mumbled_mode" in foo) rc_flags="-frotz ${rc_flags}" ;; bar) rc_flags="-baz ${rc_flags}" ;; *) warn "Invalid value for mumbled_mode" <.> return 1 <.> ;; esac run_rc_command xyzzy <.> return 0 } mumbled_plugh() <.> { echo 'A hollow voice says "plugh".' } load_rc_config $name run_rc_command "$1" .... ➊ 附加给 `$command` 的参数在 `command_args` 中进行传递。它们在 `$mumbled_flags` 之后将被添加到命令行。 其实际的执行便是此后最终的命令行传递给 `eval` 运算,输入和输出以及重定向都可以在 `command_args` 中指定。 [NOTE] ==== _永远不要_ 在 `command_args` 包含破折号选项, 类似 `-X` 或 `--foo` 这样的。`command_args` 的内容将出现在最终命令行的末尾,因此它们可能是紧接在 `${name}_flags` 中所列出的参数后面; 但大多的命令将不能识别出普通参数后的破折号选项。 更好的传递附加给 `$command` 的选项的方式是添加它们到 `${name}_flags` 的起始处。另一种方法是像后文所示的那样来修改 `rc_flags`。 ==== ➋ 一个得体的守护进程会创建一个 _pidfile_ 进程文件, 以使其进程能够更容易更可靠地被找到。如果设置了 `pidfile` 变量,告诉 man:rc.subr[8] 哪里能找到供其默认方法所使用的 `pidfile` 进程文件。 [NOTE] ==== 事实上,man:rc.subr[8] 在启动一个守护进程前还会使用 pidfile 进程文件来查看它是否已经在运行。使用了 `faststart` 参数可以跳过这个检查步骤。 ==== ➌ 如果守护进程只有在确定的文件存在的情况下才可以运行, 那就将它们列到 `required_files` 中,而 man:rc.subr[8] 将在启动守护进程之前检查那些文件是否存在。 还有相关的分别用来检查目录和环境变量的 `required_dirs` 和 `required_vars` 可供使用。它们都在 man:rc.subr[8] 中有详细的说明。 [NOTE] ==== 来自 man:rc.subr[8] 的默认方法,通过使用 `forcestart` 作为脚本的参数, 可以强制性地跳过预先需要的检查。 ==== ➍ 我们可以在守护进程有异常的时候,自定义发送给守护进程的信号。 特别是,`sig_reload` 指定了使守护进程重新装载其配置的信号;默认情况也就是 `SIGHUP` 信号。 另一个信号是发送给守护进程以停止该进程;默认情况下是 `SIGTERM` 信号,但这是可以通过设置 `sig_stop` 来进行适当更改的。 [NOTE] ==== 信号名称应当以不包含 `SIG` 前缀的形式指定给 man:rc.subr[8],就如范例中所示的那样。 FreeBSD 版本的 man:kill[1] 程序能够识别出 `SIG` 前缀,不过其它系统版本的就不一定了。 ==== ➎➏ 在默认的方法前面或后面执行附加任务是很容易的。 对于我们脚本所支持的每条命令参数而言,我们可以定义 `argument_precmd` 和 `argument_postcmd` 来完成。这些 man:sh[1] 命令分别在它们各自的方法前后被调用, 显然,从它们各自的名字就能看出来。 [NOTE] ==== 如果我们需要的话,用自定义的 `argument_cmd` 改写默认的方法,并不妨碍我们仍然使用 `argument_precmd` 和 `argument_postcmd`。 特别是,前者便于检查自定义的方法, 以及执行自身命令之前所遇到更严密的条件。于是,将 `argument_precmd` 和 `argument_cmd` 一起使用,使我们合理地将检查从动作中独立了出来。 别忘了你可以将任意的有效的 man:sh[1] 表达式插入到方法和你定义的 pre- 与 post-commands 命令中。 在大部分情况下,调用函数使实际任务有好的风格, 但千万不要让风格限制了你对其幕后到底是怎么回事的思考。 ==== ➐ 如果我们愿意实现一些自定义参数, 这些参数也可被认作为我们脚本的 __命令__,我们需要在 `extra_commands` 中将它们列出并提供方法以处理它们。 [NOTE] ==== `reload` 是个特别的命令。一方面, 它有一个在 man:rc.subr[8] 中预置的方法。另一方面, `reload` 命令默认是不被提供的。 理由是并非所有的守护进程都使用同样的重载方法, 并且有些守护进程根本没有任何东西可重载的。所以显而易见, 我们需要去询问都提供了哪些的内建功能。我们可以通过 `extra_commands` 来这样做。 我们从 `reload` 的默认方法得到了什么呢? 守护进程常常在收到一个信号后重新载入它们的配置 - 一般来说,也就是 `SIGHUP` 信号。因此 man:rc.subr[8] 尝试发送一个信号给守护进程来重载它。 该信号一般预设为 `SIGHUP`, 但是如果必要的话可以通过 `sig_reload` 变量来自定义它。 ==== ➑⓮ 我们的脚本提供了两个非标准的命令, `plugh` 和 `xyzzy`。 我们看到它们在 `extra_commands` 中被列出来了, 并且现在是时候给它们提供方法了。`xyzzy` 的方法是内联的而 `plugh` 的是以 `mumbled_plugh` 形式完成的函数。 非标准命令在启动或停止的时候不被调用。 通常它们是为了系统管理员的方便。它们还能被其它的子系统所使用, 例如,man:devd[8],前提是 man:devd.conf[5] 中已经指定了。 全部可用命令的列表,当脚本不加参数地调用时,在 man:rc.subr[8] 打印出的使用方法中能够找到。例如, 这就是供学习的脚本用法的内容: [source,shell] .... # /etc/rc.d/mumbled Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll) .... ⓭ 如果脚本需要的话,它可以调用自己的标准或非标准的命令。 这可能看起来有点像函数的调用,但我们知道,命令和 shell 函数并非一直都是同样的东西。举个例子,`xyzzy` 在这里不是以函数来实现的。另外,还有应该被有序调用的 pre-command 预置命令和 post-command 后置命令。 所以脚本运行自己命令的合适方式就是利用 man:rc.subr[8], 就像范例中展示的那样。 ➒ man:rc.subr[8] 提供了一个方便的函数叫做 `checkyesno`。 它以一个变量名作为参数并返回一个为零的退出值, 当且仅当该变量设置为 `YES`,或 `TRUE`,或 `ON`,或 `1`,区分大小写;否则返回一个非零的退出值。 在第二种情况中,函数测试变量的设置为 `NO`, `FALSE`,`OFF`,或 `0`,区分大小写; 如果变量包含别的内容的话它打印一条警告信息,例如,垃圾。 切记对 man:sh[1] 而言零值意味着真而非零值意味着假。 [IMPORTANT] ==== `checkyesno` 函数使用一个 __变量名__。不要扩大含义将变量的 _值_ 传递给它; 否则它不会如你预期那样的工作。 下面是 `checkyesno` 的合理使用范围: [.programlisting] .... if checkyesno mumbled_enable; then foo fi .... 相反地,以下面的方式调用 `checkyesno` 是不会工作的 -- 至少是不会如你预期的那样: [.programlisting] .... if checkyesno "${mumbled_enable}"; then foo fi .... ==== ➓ 我们可以通过修改 `$start_precmd` 中的 `rc_flags` 来影响传递到 `$command` 的标帜。 ⓫ 某种情况下我们可能需要发出一条重要的信息,那样的话 `syslog` 可以很好地记录日志。 这可以使用下列 man:rc.subr[8] 函数来轻松完成: `debug`,`info`, `warn`,以及 `err`。 后者以指定的代码值退出脚本。 ⓬ 方法的退出值和它们的 pre-commands 预命令不只是默认被忽略掉。如果 `argument_precmd` 返回了一个非零退出值,主方法将不会被执行。依次地是, `argument_postcmd` 将不会被调用,除非主方法返回的是一个为零的退出值。 [NOTE] ==== 然而,当给一个参数使用 `force` 前缀的时候,如 `forcestart`,man:rc.subr[8] 会听从命令行指示而忽略那些退出值最后仍然调用所有的命令。 ==== [[rcng-hookup]] == 链接脚本到 rc.d 框架 当编写好了一个脚本,它需要被整合到 [.filename]#rc.d# 中去。 一个重要的步骤就是安装脚本到 [.filename]#/etc/rc.d# (对基本系统而言)或 [.filename]#/usr/local/etc/rc.d# (对ports而言)中去。在 [.filename]#bsd.prog.mk# 和 [.filename]#bsd.port.mk# 中都为此提供了方便的接口, 通常你不必担心适当的所有权限和模式。系统脚本应当是通过可以在 [.filename]#src/etc/rc.d# 找到的 [.filename]#Makefile# 安装的。Port 脚本可以像 link:{porters-handbook}#rc-scripts[Porter's Handbook] 中描述那样通过使用 `USE_RC_SUBR` 来被安装。 不过,我们应该预先考虑到我们脚本在系统启动顺序中的位置。 我们的脚本所处理的服务可能依赖于其它的服务。举个例子, 没有网络接口和路由选择的启用运行的话,一个网络守护进程是不起作用的。 即使一个服务看似什么都不需要,在基本文件系统检查挂载完毕之前也很难启动。 之前我们曾提到过 man:rcorder[8]。现在是时候来密切地关注下它了。 笼统地说,man:rcorder[8] 处理一组文件,检验它们的内容, 并从文件集合打印一个文件列表的依赖顺序到 `stdout` 标准输出。这点是用于保持文件内部的依赖信息, 而每个文件只能说明自己的依赖。一个文件可以指定如下信息: * 它 _提供_ 的 "条件" 的名字(意味着我们服务的名字); * 它 _需求_ 的 "条件" 的名字; * 应该 _先_ 运行的文件的 "条件"的名字; * 能用于从全部文件集合中选择一个子集的额外 __关键字__( man:rcorder[8] 可通过选项而被指定来包括或省去由特殊关键字所列出的文件。) 并不奇怪的是,man:rcorder[8] 只能处理接近 man:sh[1] 语法的文本文件。man:rcorder[8] 所解读的特殊行看起来类似 man:sh[1] 的注释。这种特殊文本行的语法相当严格地简化了其处理。 请查阅 man:rcorder[8] 以获取更详细的信息。 除使用 man:rcorder[8] 的特殊行以外, 脚本可以坚持将其依赖的其它服务强制性启动。当其它服务是可选的, 并因系统管理员错误地在 man:rc.conf[5] 中禁用掉该服务而使其不能自行启动时,会需要这一点。 将这些谨记在心,我们来考虑下简单结合了依赖信息增强的守护进程脚本: [.programlisting] .... #!/bin/sh # PROVIDE: mumbled oldmumble <.> # REQUIRE: DAEMON cleanvar frotz<.> # BEFORE: LOGIN <.> # KEYWORD: nojail shutdown <.> . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" start_precmd="${name}_prestart" mumbled_prestart() { if ! checkyesno frotz_enable && \ ! /etc/rc.d/frotz forcestatus 1>/dev/null 2>&1; then force_depend frotz || return 1 <.> fi return 0 } load_rc_config $name run_rc_command "$1" .... 跟前面一样,做如下详细分析: ➊ 该行声明了我们脚本所提供的 "条件" 的名字。 现在其它脚本可以用那些名字来标明我们脚本的依赖。 [NOTE] ==== 通常脚本指定一个单独的已提供的条件。然而, 并没有什么妨碍我们从列出的那些条件中指定,例如, 为了兼容性的目的。 在其它情况,主要的名称,或者说唯一的, `PROVIDE:` 条件应该与 `${name}` 相同。 ==== ➋➌ 因此我们的脚本指示了其依赖于别的脚本所提供的 "条件"。根据这些行的信息,脚本请示 man:rcorder[8] 以将其放在一个或多个提供 [.filename]#DAEMON# 和 [.filename]#cleanvar# 的脚本后面,但在提供 [.filename]#LOGIN# 的脚本前面。 [NOTE] ==== `BEFORE:` 这一行不可以在其它脚本不完整的依赖关系列表中滥用。 适合使用 `BEFORE:` 的情况是当其它脚本不关心我们的脚本, 但是我们的脚本如果在另一个之前运行的话能够更好地执行任务。 一个典型的实例是网络接口和防火墙: 虽然接口不依赖防火墙来完成自己的工作, 但是系统安全将因一切网络流量之前启动的防火墙而受益。 除了条件相对应的每个单独服务,脚本使用元条件和它们的 "占位符" 来保证某个操作组在其它之前被执行。 这些是由 [.filename]#UPPERCASE# 大写名字所表示的。它们的列表和用法可以在 man:rc[8] 中找到。 切记将一个服务名称放进 `REQUIRE:` 行不能保证实际的服务会在我们的脚本启动的时候运行。 所需求的服务可能会启动失败或在 man:rc.conf[5] 中被禁掉了。 显然,man:rcorder[8] 是无法追踪这些细节的,并且 man:rc[8] 也不会去追踪。所以, 脚本启动的应用程序应当能够应付任何所需求的服务的不可用情况。 某些情况下,我们可以用 <> 所讨论的方式来协助脚本。 ==== ➍ [[keywords]]如我们从上述文字所记起的,man:rcorder[8] 关键字可以用来选择或省略某些脚本。即任何 man:rcorder[8] 用户可以通过指定 `-k` 和 `-s` 选项来分别指定 "保留清单(keep list)" 和 "跳过清单(skip list)"。 从全部文件到按依赖关系排列的清单,man:rcorder[8] 将只是挑出保留清单(除非是空的) 中那些带关键字的以及从跳过清单中挑出不带关键字的文件。 在 FreeBSD 中,man:rcorder[8] 被 [.filename]#/etc/rc# 和 [.filename]#/etc/rc.shutdown# 所使用。 这两个脚本定义了 FreeBSD 中 [.filename]#rc.d# 关键字以及它们的意义的标准列表如下: ➎ [[forcedep]]以 `force_depend` 起始的行应被用于更谨慎的情况。通常,用于修正相互关联的 [.filename]#rc.d# 脚本分层结构的配置文件时会更加稳妥。 如果你仍不能完成不含 `force_depend` 的脚本, 范例提供了一个如何有条件地调用它的习惯用法。在范例中,我们的 `mumbled` 守护进程需求另一个以高级方式启动的进程, `frotz`。但 `frotz` 也是可选的; 而且 man:rcorder[8] 对这些信息是一无所知的。幸运的是, 我们的脚本已访问到全部的 man:rc.conf[5] 变量。如果 `frotz_enable` 为真,我们希望的最好结果是依靠 [.filename]#rc.d# 已经启动了 `frotz`。 否则我们强制检查 `frotz` 的状态。最终, 如果 `frotz` 依赖的服务没有找到或运行的话, 我们将强制其运行。这时 `force_depend` 将发出一条警告信息,因为它只应该在检查到配置信息丢失的情况下被调用。 [[rcng-args]] == 给予 rc.d 脚本更多的灵活性 当进行启动或停止的调用时,[.filename]#rc.d# 脚本应该作用于其所负责的整个子系统。例如, [.filename]#/etc/rc.d/netif# 应该启动或停止 man:rc.conf[5] 中所描述的全部网络接口。每个任务都唯一地听从一个如 `start` 或 `stop` 这样的单独命令参数的指示。在启动和停止之间的时间, [.filename]#rc.d# 脚本帮助管理员控制运行中的系统, 并其在需要的时候它将产生更多的灵活性和精确性。举个例子, 管理员可能想在 man:rc.conf[5] 中添加一个新网络接口的配置信息, 然后在不妨碍其它已存在接口的情况下将其启动。 在下次管理员可能需要关闭一个单独的网络接口。在魔幻的命令行中, 对应的 [.filename]#rc.d# 脚本调用一个额外的参数, 网络接口名即可。 幸运的是,man:rc.subr[8] 允许传递任意多(取决于系统限制)的参数给脚本的方法。 由于这个原因,脚本自身的改变可以说是微乎其微。 man:rc.subr[8] 如何访问到附加的命令行参数呢?直接获取么? 并非是无所不用其极的。首先,man:sh[1] 函数没有访问到调用者的定位参数,而 man:rc.subr[8] 只是这些函数的容器。其次,[.filename]#rc.d# 指令的一个好的风格是由主函数来决定将哪些参数传递给它的方法。 所以 man:rc.subr[8] 提供了如下的方法: `run_rc_command` 传递其所有参数但将第一个参数逐字传递到各自的方法。首先, 发出以方法自身为名字的参数:`start`, `stop`,等等。这会被 `run_rc_command` 移出, 这样命令行中原本 `$2` 的内容将作为 `$1` 来提供给方法,等等。 为了说明这点,我们来修改原来的虚拟脚本, 这样它的信息将取决于所提供的附加参数。从这里出发: [.programlisting] .... #!/bin/sh . /etc/rc.subr name="dummy" start_cmd="${name}_start" stop_cmd=":" kiss_cmd="${name}_kiss" extra_commands="kiss" dummy_start() { if [ $# -gt 0 ]; then <.> echo "Greeting message: $*" else echo "Nothing started." fi } dummy_kiss() { echo -n "A ghost gives you a kiss" if [ $# -gt 0 ]; then <.> echo -n " and whispers: $*" fi case "$*" in *[.!?]) echo ;; *) echo . ;; esac } load_rc_config $name run_rc_command "$@" <.> .... 能注意到脚本里发生了那些实质性改变么? ➊ 你输入的所有在 `start` 之后的参数可以被当作各自方法的定位参数一样被终结。 我们可以根据我们的任务、技巧和想法来以任何方式使用他们。 在当前的例子中,我们只是以下行中字符串的形式传递参数给 man:echo[1] 程序 - 注意 `$*` 是有双引号的。这里是脚本如何被调用的: [source,shell] .... # /etc/rc.d/dummy start Nothing started. # /etc/rc.d/dummy start Hello world! Greeting message: Hello world! .... ➋ 同样用于我们脚本提供的任何方法,并不仅限于标准的方法。 我们已经添加了一个自定义的叫做 `kiss` 的方法, 并且它给附加参数带来的戏耍决不亚于 `start`。 例如: [source,shell] .... # /etc/rc.d/dummy kiss A ghost gives you a kiss. # /etc/rc.d/dummy kiss Once I was Etaoin Shrdlu... A ghost gives you a kiss and whispers: Once I was Etaoin Shrdlu... .... ➌ 如果我们只是传递所有附加参数给任意的方法, 我们只需要在脚本的最后一行我们调用 `run_rc_command` 的地方, 用 `"$@` 代替 `"$1"` 即可。 [IMPORTANT] ==== 一个 man:sh[1] 程序员应该是可以理解 `$*` 和 `$@` 的微妙区别只是指定全部定位参数的不同方法。 关于此更深入的探讨,可以参考这个很好的 man:sh[1] 脚本编程手册。在你完全理解这些表达式的意义之前请不要使用它们, 因为误用它们将给脚本引入缺陷和不安全的弊端。 ==== [NOTE] ==== 现在 `run_rc_command` 可能有个缺陷, 它将影响保持参数之间的原本边界。也就是, 带有嵌入空白的参数可能不会被正确处理。该缺陷是由于对 `$*` 的误用。 ==== [[rcng-furthur]] == 进一步阅读 [[lukem]]http://www.mewburn.net/luke/papers/rc.d.pdf[Luke Mewburn 的原始文章] 中讲述了 [.filename]#rc.d# 的基本概要, 并详细阐述了其设计方案的原理。该文章提供了深入了解整个 [.filename]#rc.d# 框架以及其所在的现代 BSD 操作系统的内容。 [[manpages]]在 man:rc[8],man:rc.subr[8], 还有 man:rcorder[8] 的联机手册中,对 [.filename]#rc.d# 组件做了非常详细的记载。 在你写脚本时,如果不去学习和参考这些联机手册的话, 你是无法完全发挥出 [.filename]#rc.d# 的能量的。 工作中实际范例的主要来源就是运行的系统中的 [.filename]#/etc/rc.d# 目录。 它的内容可读性非常好,因为大部分的枯燥的内容都深藏在 man:rc.subr[8] 中了。切记 [.filename]#/etc/rc.d# 的脚本也不是神仙写出来的, 所以它们可能也存在着代码缺陷以及低级的设计方案。 但现在你可以来改进它们了! diff --git a/documentation/content/zh-cn/articles/remote-install/_index.adoc b/documentation/content/zh-cn/articles/remote-install/_index.adoc index b14208aefa..dfbd64cd6b 100644 --- a/documentation/content/zh-cn/articles/remote-install/_index.adoc +++ b/documentation/content/zh-cn/articles/remote-install/_index.adoc @@ -1,323 +1,335 @@ --- title: FreeBSD 操作系统在无远程控制台下的远程安装 authors: - author: Daniel Gerzo email: danger@FreeBSD.org copyright: 2008 The FreeBSD Documentation Project releaseinfo: "$FreeBSD$" trademarks: ["freebsd", "general"] --- = FreeBSD 操作系统在无远程控制台下的远程安装 :doctype: article :toc: macro :toclevels: 1 :icons: font :sectnums: :sectnumlevels: 6 :source-highlighter: rouge :experimental: :toc-title: 目录 :part-signifier: 部分 :appendix-caption: 附录 :table-caption: 表 :figure-caption: 图 :example-caption: 例 +ifeval::["{backend}" == "html5"] include::shared/authors.adoc[] include::shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "pdf"] +include::../../../../shared/authors.adoc[] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] + +ifeval::["{backend}" == "epub3"] +include::../../../../shared/authors.adoc[] +include::../../../../shared/zh-cn/urls.adoc[] +endif::[] [.abstract-title] 摘要 本文归档了当远程控制台不可用的情况下 FreeBSD 操作系统的远程安装。 文章背后的主要灵感归功于和 `{mm-name}` 还有由 `{pjd-name}` 提供的宝贵输入合作的结果。 ''' toc::[] [[background]] == 背景 世界上有很多的服务器主机供应商, 但是他们中只有很少的一部分正式支持 FreeBSD, 他们通常为他们提供的服务器上安装 Linux(R) 发行版提供支持。 在某些情况下,如果你请求这些公司他们会安装一个你首选的 Linux(R) 发行版。有了这个选择,我们将试图安装 FreeBSD。 在其他情况下,他们可能提供一个急救系统用于紧急情况。 使用这个可能将有利于我们的目的更好的实现。 本文涵盖了引导一个包含 RAID-1 及 ZFS 性能的 FreeBSD 系统的远程安装的基本安装配置所必须的步骤。 [[intro]] == 简介 这一节会摘要本文的目的以及更好阐述这里所概括的东西。 本文中的这些指令将有益于那些使用不支持 FreeBSD 的托管设施提供的服务的人。 [.procedure] . 如我们提到过的 <> 的那一节,许多的有声望的服务器主机托管公司提供了各种的急救系统。 可以从他们自己的 `局域网` 启动并可以通过 SSH 访问。 他们通常提供这种支持目的用于帮助他们的顾客修正损坏的操作系统。 如文章将说明的,我们将能够通过这些急救系统的帮助来安装 FreeBSD。 + . 文章的下一小节会描述如何配置,并在本地机器上构建最小限度的 FreeBSD。该版本最终会从随机存储盘运行到远程机器上面去。 这将允许我们使用 Sysinstall 实用程序从一个 FTP 镜像安装一套完整的 FreeBSD 操作系统。 . 文章的剩余内容除了描述 ZFS 文件系统的配置还将描述系统本身的安装步骤。 [[requirements]] === 需求 想要成功地做下去,你必须: * 拥有一个可通过 SSH 网络访问的操作系统。 * 理解 FreeBSD 的安装过程 * 熟悉 man:sysinstall[8] 实用程序 * 拥有 FreeBSD 安张的 ISO 镜像文件或者易于使用的 CD [[preparation]] == 准备工作 - mfsBSD 在 FreeBSD 可能安装到目标系统上之前, 需要先构建一个最小化的从磁盘启动的 FreeBSD 操作系统映像文件。 此方法中新系统必须能够从网络访问, 并且安装的其他过程能够在没有远程访问到系统控制台的情况下完成。 mfsBSD 设置工具能够被用来构建一个微小的 FreeBSD 映像。如 mfsBSD 名字的含义 ("mfs" 的意思是 "memory file system" 内存文件系统), 最后的映像全部从随机存储器运行。多亏了这个特性, 磁盘的操作将不会有任何限制,因此它能够被用来安装一个完整的 FreeBSD 操作系统。 mfsBSD 的主页在 link:http://people.freebsd.org/\~mm/mfsbsd/[http://people.freebsd.org/~mm/mfsbsd/], 包含了指向最新释出的设置工具。 请注意关于 mfsBSD 内幕以及它所有的适用都超出了本文的内容, 感兴趣的读者应该去查阅 mfs 的原始文档得到更多详细内容。 下载并解压出最新的 mfsBSD 版本,并改变自己的当前工作目录到存在 mfsBSD 脚本文件的目录: [source,shell] .... # fetch http://people.freebsd.org/~mm/mfsbsd/mfsbsd-latest.tar.gz # tar xvzf mfsbsd-1.0-beta1.tar.gz # cd mfsbsd-1.0-beta1/ .... [[mfsbsd-config]] === mfsBSD 的配置 引导 mfsBSD 之前, 必须设置一些重要的配置选项。 最重要的是我们必须有正确地,自然地,网络配置。 最适合的方法配置网络选项取决于我们是否事先知道我们会用到的网络接口, 而且网络接口驱动程序应被系统为我们的硬件载入。 我们将看到 mfsBSD 如何能够在任一种情况下被配置。 另外一件重要的事情是设置 `root` 的密码。 这将通过编辑 [.filename]#conf/rootpw.conf# 文件来完成。 请记住该文件将把你的密码保存在简单的文本中, 所以在此我们不推荐你使用真实的密码。然而, 这只是一个临时使用一次的密码,你可以在随后安装好的系统中更改它。 ==== 编辑 [.filename]#conf/interfaces.conf# 的方法 如果我们安装好的网卡是未知类型的, 我们可以使用 mfsBSD 的自动探测功能。 mfsBSD 启动脚本能够探测到正确的驱动来使用, 基于网络接口的 MAC 地址,我们假设在 [.filename]#conf/interfaces.conf# 文件中设置如下选项: [.programlisting] .... initconf_interfaces="ext1" initconf_mac_ext1="00:00:00:00:00:00" initconf_ip_ext1="192.168.0.2" initconf_netmask_ext1="255.255.255.0" .... 别忘了添加 `defaultrouter` 信息到 [.filename]#conf/rc.conf# 文件中: [.programlisting] .... defaultrouter="192.168.0.1" .... ==== 编辑 [.filename]#conf/rc.conf# 的方法 当网络接口的驱动是已知类型的,使用 [.filename]#conf/rc.conf# 文件添加联网选项会更加方便。 该文件的语法跟 FreeBSD 中标准的 man:rc.conf[5] 文件的语法相同。 例如,当你知道被使用的将是一个 man:re[4] 网络接口设备, 你可以在 [.filename]#conf/rc.conf# 文件中设置如下选项: [.programlisting] .... defaultrouter="192.168.0.1" ifconfig_re0="inet 192.168.0.2 netmask 255.255.255.0" .... [[mfsbsd-build]] === 构建一个 mfsBSD 映像 构建一个 mfsBSD 映像文件的过程是非常简单明了的。 第一步是挂载 FreeBSD 的安装 CD, 或者挂载安装 ISO 文件到 [.filename]#/cdrom#。 因为例子的缘故,在文章中我们将假定你下载的是 FreeBSD 7.0-RELEASE ISO 文件。使用 man:mdconfig[8] 实用程序挂载 ISO 映像文件到 [.filename]#/cdrom# 目录非常简单: [source,shell] .... # mdconfig -a -t vnode -u 10 -f 7.0-RELEASE-amd64-disc1.iso # mount_cd9660 /dev/md10 /cdrom .... 紧接着,构建可启动的 mfsBSD 映像: [source,shell] .... # make BASE=/cdrom/7.0-RELEASE .... [NOTE] ==== 上面的 make 命令必须在 mfsBSD 目录树的最高一层运行,也就是: [.filename]#~/mfsbsd-1.0-beta1/#。 ==== === 启动 mfsBSD 现在 mfsBSD 映像已经准备好了, 必须把它上传到远程的一个正在运行的急救系统上或者一个预安装了 Linux(R) 发行版的系统上。最适合做这个工作的工具是 scp: [source,shell] .... # scp disk.img root@192.168.0.2:. .... 想要正确的引导 mfsBSD 映像, 必须把它安放在机器的第一块(可启动)设备上。 这可能会和使用的例子我们假定的一样,第一块可启动磁盘设备是 [.filename]#sda#: [source,shell] .... # dd if=/root/disk.img of=/dev/sda bs=1m .... 如果一切正常,该映像现在应该存在于第一块设备的 MBR(主引导区)而机器也应该能够被启动了。 使用工具 man:ping[8] 来查看机器是否被正确启动。 一旦它回复在线状态,就应该能够使用 `root` 用户和配置好的密码通过 man:ssh[1] 来访问它了。 [[installation]] == FreeBSD 操作系统的安装 mfsBSD 成功被引导后它就应该能够通过 man:ssh[1] 登入了。这一节会描述如何创建 slices 并标记 slices 的 label, 为 RAID-1 配置 gmirror, 还有如何使用 Sysinstall 来安装一个最小的FreeBSD操作系统版本。 === 准备磁盘 首要的任务是为 FreeBSD 分配磁盘空间,也就是, 创建 slices 和 partitions。很显然, 当前运行的系统是全部被载入到系统内存中的因此操作磁盘将没有任何问题。 要完成这个任务,可以是使用 Sysinstall 或者 man:fdisk[8] 中的二者任一并结合工具 man:bsdlabel[8]。 在开始时,将所有磁盘都标记成空的, 在每个磁盘上重复如下命令: [source,shell] .... # dd if=/dev/zero of=/dev/ad0 count=2 .... 下面,使用你喜欢的工具创建 slices 并标记磁盘 label。 比较简单的方法是使用 Sysinstall, 强大也可能几乎没有漏洞方法是使用标准的基于文本的 UNIX(R) 工具, 类似于 man:fdisk[8] 和 man:bsdlabel[8] 这些工具的使用也会在这一节中包括。前者已经被包括在 FreeBSD 手册的 link:{handbook}#install-steps[安装FreeBSD] 一章中了。如本节中刚提到的,这篇文章会展示如何设置一个带有 RAID-1 和 ZFS 性能的系统。我们的设置由一个小工具 man:gmirror[8] 镜像为 [.filename]#/# (root), [.filename]#/usr# 和 [.filename]#/var# 文件系统, 并把剩余的磁盘空间被分配为 man:zpool[8] 镜像出的 ZFS 文件系统。请注意, ZFS 文件系统将在 FreeBSD 操作系统成功安装并启动后才会被配置。 下面的例子会描述如何去创建 slices 和 labels, 在每个 partition 上初始化 man:gmirror[8] 并如何在每个被镜像过的 partition 上创建 UFS2 文件系统: [source,shell] .... # fdisk -BI /dev/ad0 <.> # fdisk -BI /dev/ad1 # bsdlabel -wB /dev/ad0s1 <.> # bsdlabel -wB /dev/ad1s1 # bsdlabel -e /dev/ad0s1 <.> # bsdlabel /dev/ad0s1 > /tmp/bsdlabel.txt && bsdlabel -R /dev/ad1s1 /tmp/bsdlabel.txt <.> # gmirror label root /dev/ad[01]s1a <.> # gmirror label var /dev/ad[01]s1d # gmirror label usr /dev/ad[01]s1e # gmirror label -F swap /dev/ad[01]s1b <.> # newfs /dev/mirror/root <.> # newfs /dev/mirror/var # newfs /dev/mirror/usr .... <.> 在整个磁盘上创建一个 slice 并初始化包含在磁盘第一个扇区启动代码。 重复在系统上全部的磁盘上执行此命令。 <.> 为每块磁盘写入一个包括启动代码的内容的标准 label。 <.> 现在,手动去编辑磁盘的 label。可以查阅 man:bsdlabel[8] 的联机手册来找到如何建立 partitions 的方法。创建如下 partions,`a` 为 [.filename]#/# (root) 文件系统, `b` 为 swap 交换空间, `d` 为 [.filename]#/usr# 还有最后 `f` 被用于 ZFS。 <.> 引入你刚才创建的 label 到第二块磁盘, 所以两块磁盘会使用同样的 label。 <.> 在每个 partition 上初始化 man:gmirror[8]。 <.> 注意 `-F` 选项被用在 swap 交换分区的 partition。 man:gmirror[8] 这个指令认为设备处于可靠的状态除非电源系统故障。 <.> 在每个被镜像的分区上创建一个 UFS2 的文件系统。 === 系统安装 这是最重要的一部分。 此节将描述如何在我们上一小节已经准备好的磁盘上安装一个最小的 FreeBSD 版本。要达成这个目的,所有的文件安系统需要被挂载乃至于 Sysinstall 可以把 FreeBSD 系统的内容写到磁盘上: [source,shell] .... # mount /dev/mirror/root /mnt # mkdir /mnt/var /mnt/usr # mount /dev/mirror/var /mnt/var # mount /dev/mirror/usr /mnt/usr .... 当你做完这些时,打开 man:sysinstall[8]。 从主菜单选择自定义 [.guimenuitem]#Custom# 安装。 选中 [.guimenuitem]#Options# 选项然后按回车确认。 使用方向键获取帮助,移动鼠标指针到 `Install Root` 选项,按 kbd:[空格] 更改为 [.filename]#/mnt#。 按 kbd:[回车] 提交你的更改并使用 kbd:[q] 退出 [.guimenuitem]#Options# (选项)菜单。 [WARNING] ==== 注意这一步骤非常重要,如果被跳过了, Sysinstall 将不能安装 FreeBSD。 ==== 到 [.guimenuitem]#Distributions#(发行版)菜单选项, 使用方向键移动鼠标指针到 `Minimal`(最小化)选项, 并使用 kbd:[空格键] 选中该选项。 本文使用了最小版本来保存网络联通信息,因为系统本身会通过 ftp 来安装。使用 `Exit`(退出)选项退出这个菜单。 [NOTE] ==== [.guimenuitem]#Partition# 和 [.guimenuitem]#Label# 菜单将被跳过, 这些没有多少价值了。 ==== [.guimenuitem]#Media#(媒介)菜单, 选择 `FTP` 选项。 选择一个距离你最近的镜像站点并交给 Sysinstall 假定网络已经配置完好。你将再回到 [.guimenuitem]#Custom# (自定义)菜单。 最后,选择最后的选项来执行系统的安装过程, [.guimenuitem]#Commit#, 当安装完成后退出 Sysinstall 即可。 === 后期安装步骤 FreeBSD 操作系统现在应该安装完毕了;通常情况下, 安装过程还没有结束。还需要进行一些安装后期的步骤使得容许 FreeBSD 在将来启动并能够登入系统。 你现在必须 man:chroot[8] 到刚安装的全新的系统中来完成安装。 使用如下命令: [source,shell] .... # chroot /mnt .... 要达到我们的目的,进行如下步骤: * 拷贝 `GENERIC`(通用)内核到 [.filename]#/boot/kernel# 目录: + [source,shell] .... # cp -Rp /boot/GENERIC/* /boot/kernel .... * 创建 [.filename]#/etc/rc.conf#, [.filename]#/etc/resolv.conf# 还有 [.filename]#/etc/fstab# 文件。 不要忘记正确地设置网络信息并在 [.filename]#/etc/rc.conf# 文件中启用 sshd。 [.filename]#/etc/fstab# 文件内容类似于下面的内容: + [.programlisting] .... # Device Mountpoint FStype Options Dump Pass# /dev/mirror/swap none swap sw 0 0 /dev/mirror/root / ufs rw 1 1 /dev/mirror/usr /usr ufs rw 2 2 /dev/mirror/var /var ufs rw 2 2 /dev/cd0 /cdrom cd9660 ro,noauto 0 0 .... * 创建 [.filename]#/boot/loader.conf# 文件,并写入如下内容: + [.programlisting] .... geom_mirror_load="YES" zfs_load="YES" .... * 执行下面的命令,使得 ZFS 在下次启动后可用: + [source,shell] .... # echo 'zfs_enable="YES"' >> /etc/rc.conf .... * 可以用 man:adduser[8] 工具来添加额外的用户。 不要忘记添加一个用户到 `wheel` 组,这样你可以在重新启动后获得 root 权限。 * 反复检验你的设置是否正确。 现在你的系统在下次启动后应该可用了。使用 man:reboot[8] 命令重新启动你的系统。 [[zfs]] == ZFS 如果你的系统重新启动后还完好,现在应该能够登入了。 欢迎来到崭新的 FreeBSD 安装,进行远程的不使用远程控制台的安装。 最后还剩下的步骤是配置 man:zpool[8] 并创建一些 man:zfs[8] 文件系统。建立并管理 ZFS 非常简单。 首先,创建一个镜像的pool: [source,shell] .... # zpool create tank mirror /dev/ad[01]s1f .... 再接着,创建一些文件系统: [source,shell] .... # zfs create tank/ports # zfs create tank/src # zfs set compression=gzip tank/ports # zfs set compression=on tank/src # zfs set mountpoint=/usr/ports tank/ports # zfs set mountpoint=/usr/src tank/src .... 这就是全部步骤了。如果你对 FreeBSD 上的 ZFS 感兴趣,请查阅 FreeBSD WIKI 中的 http://wiki.freebsd.org/ZFS[ZFS] 一节。