Language tweaks.

Submitted by:           JU Pengfei <jupengfei msn com>
Merge work done by:     delphij@
Obtained from:          The FreeBSD Simplified Chinese Project
This commit is contained in:
Fukang Chen 2009-12-11 15:58:42 +00:00
parent 6fb3926644
commit c6c9e87b28
Notes: svn2git 2020-12-08 03:00:23 +00:00
svn path=/head/; revision=35048

View file

@ -11,7 +11,7 @@
<article>
<articleinfo>
<title>BSD中实用的rc.d脚本编程</title>
<title>BSD rc.d脚本编程实战</title>
<author>
<firstname>Yar</firstname>
@ -40,107 +40,107 @@
</legalnotice>
<abstract>
<para>初学者可能会发现,通过正式的文档资料
根据实际情况在 BSD 的 <filename>rc.d</filename>
框架上进行实用的 <filename>rc.d</filename> 脚本编程比较困难
本文中,我们采用一些复杂性不断增加的典型案例,
<para>初学者可能会发现,难以通过正式的文档,
基于 BSD 的 <filename>rc.d</filename>
框架,编写一些实际任务的 <filename>rc.d</filename> 脚本
本文中,我们采用一些复杂性不断增加的典型案例,
来展示适合每个案例的 <filename>rc.d</filename> 特性,
并探讨它们如何运行工作的
一测验将为大家进一步学习设计有效的
<filename>rc.d</filename> 应用程序提供了有价值的参考。</para>
并探讨其中的工作原理
样的实验为大家进一步研究设计有效的
<filename>rc.d</filename> 应用程序提供了一些参考点。</para>
</abstract>
</articleinfo>
<sect1 id="rcng-intro">
<title>简介</title>
<para>历史上 BSD 曾有过一个单独整体的启动脚本,
<para>历史上 BSD 曾有过一个单的启动脚本,
<filename>/etc/rc</filename>。 该脚本在系统启动的时候被
&man.init.8; 程序所引导,并执行所有多用户操作所需求的用户级任务:
检查并挂载文件系统,设置网络,启动守护进程,等等。
在每个系统中实际的任务列表也并不相同;
管理员需要根据需求自定义这样的任务列表。在一些非常规的情况中,
必须修改 <filename>/etc/rc</filename> 文件,
在每个系统中实际的任务清单也并不相同;
管理员需要根据需求自定义这样的任务清单。在一些特殊的情况中,
不得不去修改 <filename>/etc/rc</filename> 文件,
一些真正的黑客乐此不疲。</para>
<para>整体单独脚本启动方法的真正问题是它没有提供对从
<para>单一脚本启动方法的真正问题是它没有提供对从
<filename>/etc/rc</filename> 启动的单个组件的控制。
拿一个例子来说吧,<filename>/etc/rc</filename>
不能够重新启动个单独的守护进程。
不能够重新启动个单独的守护进程。
系统管理员不得不手动找出守护进程,并杀掉它,
等待它真正退出后,再通过 <filename>/etc/rc</filename>
浏览得到该守护进程的标识,最终输入全部命令来再次启动守护进程。
如果重新启动的服务包括不止一个守护进程或需要更多动作的话,
该任务将变得更加困难容易出错。简而言之,
个整体脚本在实现我们这样的目的上是不及格的:
让系统管理员的生活更轻松。</para>
等待它真正退出后,再通过浏览 <filename>/etc/rc</filename>
得到该守护进程的标识,最终输入全部命令来再次启动守护进程。
如果重新启动的服务包括不止一个守护进程或需要更多动作的话,
该任务将变得更加困难以及容易出错。简而言之,
一脚本在实现我们这样的目的上是不成功的:
让系统管理员的生活更轻松。</para>
<para>再后来,为了将最重要的一些子系统单独划分开来,
人们试着从 <filename>/etc/rc</filename> 文件中分离出来一些部分
总所周知的例子就是用来启动联网的 <filename>/etc/netstart</filename>
<para>再后来,为了将最重要的一些子系统独立出来,
便尝试将部分的内容从 <filename>/etc/rc</filename> 分离出来了
最广为人知的例子就是用来启动联网的 <filename>/etc/netstart</filename>
文件。它容许从单用户模式访问网络,
但由于它的部分代码需要和一些与联网基本无任何关系的动作交互,
所以它并没有能够完美地结合到自启动的进程中。那便是为何
但由于它的部分代码需要和一些与联网完全无关的动作交互,
所以它并没有完美地结合到自启动的进程中。那便是为何
<filename>/etc/netstart</filename> 被演变成
<filename>/etc/rc.network</filename> 的原因了。
后者不再是一个普通的脚本;它包括了庞大的,由
<filename>/etc/rc</filename> 在不同的系统启动级中调用的凌乱的
&man.sh.1; 函数。然而,当启动任务变得多样化以及经更改,
<quote>类模块化</quote> 方法变得比曾的整体
<filename>/etc/rc</filename> 更加的缓慢费事。</para>
<filename>/etc/rc</filename> 在不同的系统启动级中调用的凌乱的
&man.sh.1; 函数。然而,当启动任务变得多样化以及经更改,
<quote>类模块化</quote> 方法变得比曾的整体
<filename>/etc/rc</filename> 更缓慢费事。</para>
<para>由于没有一个干净和易于设计的框架,
启动脚本不得不拼命来满足飞速开发中基于 BSD 的操作系统的需求。
变得清晰并经过许多必须的步骤最终变成一个具有细密性和扩展性的
启动脚本不得不全力更改以满足飞速开发中基于 BSD 的操作系统的需求。
逐渐变得明朗并经过许多必要的步骤最终变成一个具有细密性和扩展性的
<filename>rc</filename> 系统。BSD <filename>rc.d</filename>
就这样诞生了。Luke Mewburn 和 NetBSD 社区是公认的
<filename>rc.d</filename> 之父。再之后它被引入到了 &os; 中。
它的名字引用为系统单独的服务脚本的位置,也就是
<filename>/etc/rc.d</filename>下面的那些脚本。
之后我们将学习到更多关于 <filename>rc.d</filename>
之后我们将学习到更多 <filename>rc.d</filename>
系统的组件并看看单个脚本是如何被调用的。</para>
<para>BSD <filename>rc.d</filename>
背后的基本理念是 <emphasis>良好</emphasis> 的模块性和代码重用
<emphasis>良好</emphasis> 的模块意味着每个基本
背后的基本理念是 <emphasis>良好</emphasis> 的模块化和代码重用性
<emphasis>良好</emphasis> 的模块意味着每个基本
<quote>服务</quote> 就象系统守护进程或原始启动任务那样,
通过它们所拥有的能够启动该服务的 &man.sh.1; 脚本,来停止服务,
重载服务,检查服务的状态。具体动作由脚本的命令行参数所决定。
通过属于它们的可启动该服务的 &man.sh.1; 脚本,来停止服务,
重载服务,检查服务的状态。具体动作由脚本的命令行参数所决定。
<filename>/etc/rc</filename> 脚本仍然掌管着系统的启动,
但现在它仅仅是使用 <option>start</option> 参数来一个个调用那些小的脚本。
易于使用正在运行中具有同样设定的脚本的 <option>stop</option>
参数来很好地执行停止任务,这是被 <filename>/etc/rc.shutdown</filename>
脚本所完成的。注意这是多么接近地遵循了 Unix 的哲学:
便于用 <option>stop</option> 来对运行中的同样的脚本很好地执行停止任务,
这是被 <filename>/etc/rc.shutdown</filename>
脚本所完成的。看,这是多么好地体现了 Unix 的哲学:
拥有一组小的专用的工具,每个工具尽可能好地完成自己的任务。
<emphasis>代码重用</emphasis> 意味着所有的通用操作由
<filename>/etc/rc.subr</filename> 中的一些 &man.sh.1; 函数所实现。
现在一个典型的脚本只需要寥寥几行的 &man.sh.1; 代码。最终,
现在一个典型的脚本只需要寥寥几行的 &man.sh.1; 代码。最终,
&man.rcorder.8; 成为了 <filename>rc.d</filename> 框架中重要的一部分,
它用来帮助 <filename>/etc/rc</filename>
根据小脚本之间的相互依赖关系并有顺序地运行它们。它同样帮助
处理小脚本之间的依赖关系并有次序地运行它们。它同样帮助
<filename>/etc/rc.shutdown</filename> 做类似的事情,
因为正确的关闭次序是相对于启动的次序的。</para>
<para>BSD <filename>rc.d</filename> 的设计在
<link linkend="lukem"> Luke Mewburn 的原文 </link> 中有描述
<link linkend="lukem"> Luke Mewburn 的原文 </link> 中有记录
以及 <filename>rc.d</filename> 组件也被充分详细地记录在各自的
<link linkend="manpages">联机手册</link> 中。然而,
它可能没能清晰展现给一个 <filename>rc.d</filename>
新手如何将无数的块和片关联起来以为具体的任务创建一个好样式的脚本。
因此本文将试着用不同的方式来描述 <filename>rc.d</filename>。
它将展示出应该用在许多典型情况中的那些特性,并阐述了为何是这样
注意这并不是一篇 how-to 文档,因为我们的目的不是给出现成的配方,
而是在展示一些简单的进入 <filename>rc.d</filename> 的范围的门
新手,如何将无数的块和片进行关联来为具体的任务创建一个好风格的脚本。
因此本文将试着以不同的方式来讲述 <filename>rc.d</filename>。
它将展示在某些典型情况中应该使用哪些特性,并阐述了为何如此
注意这并不是一篇 how-to 文档,我们的目的不是给出现成的配方,
而是在展示一些简单的进入 <filename>rc.d</filename> 的范围的门
本文也不是相关联机手册的替代品。
阅读本文时不要忘记参考联机手册以获取更正规完整的文档。</para>
阅读本文时记得同时参考联机手册以获取更完整正规的文档。</para>
<para>理解本文是有一些先决条件的。首先,你应当熟悉
&man.sh.1; 脚本变成语言以掌握 <filename>rc.d</filename>
还有,你应当知系统是如何执行用户级的启动和停止任务,这些在
&man.rc.8; 中都有所描述。</para>
<para>理解本文需要一些先决条件。首先,你需要熟悉
&man.sh.1; 脚本编程语言以掌握 <filename>rc.d</filename>
还有,你需要知道系统是如何执行用户级的启动和停止任务,这些在
&man.rc.8; 中都有说明。</para>
<para>本文关注的是 <filename>rc.d</filename> 的 &os; 分支。
然而,它可能对 NetBSD 的开发者也同样有用,因为 BSD
不过,它可能对 NetBSD 的开发者也同样有用,因为 BSD
<filename>rc.d</filename> 的两个分支不只是共享了同样的设计,
还保留了对脚本编写者都可见的类似观点。</para>
</sect1>
@ -150,8 +150,8 @@
<para>在开始打开 <envar>$EDITOR</envar>(编辑器)
之前进行小小的思考不是坏事。为了给一个系统服务写一个
<quote>脾气好的</quote> <filename>rc.d</filename> 脚本,
我们应该首先应该能回答以下问题:</para>
<quote>听话的</quote> <filename>rc.d</filename> 脚本,
我们首先应该能回答以下问题:</para>
<itemizedlist>
<listitem>
@ -159,16 +159,15 @@
</listitem>
<listitem>
<para>脚本将作为一个单独的程序,例如,
一个守护进程,还是执行更复杂的动作?</para>
<para>脚本将为单个程序服务,如一个守护进程,还是执行更复杂的动作?</para>
</listitem>
<listitem>
<para>我们的服务依赖哪些服务?反过来</para>
<para>我们的服务依赖哪些服务?反过来哪些服务依赖我们的服务</para>
</listitem>
</itemizedlist>
<para>从下面的例子中我们将看到为什么说知道这些问题的答案是很重要的。</para>
<para>从下面的例子中我们将看到为什么说知道这些问题的答案是很重要的。</para>
</sect1>
<sect1 id="rcng-dummy">
@ -204,28 +203,27 @@ run_rc_command "$1"<co id="rcng-dummy-runcommand"></programlisting>
假如再有可执行位的设置,
脚本就能象一个二进制程序一样被精确地调用执行。
(请参考 &man.chmod.1;。) 例如,
一个系统管理员可以手动运行我们的脚本,
从命令行中:</para>
一个系统管理员可以从命令行手动运行我们的脚本:</para>
<screen>&prompt.root; <userinput>/etc/rc.d/dummy start</userinput></screen>
<note>
<para>为了使 <filename>rc.d</filename> 框架正确地管理脚本,
它的脚本需要用 &man.sh.1; 语言编写。
如果你有一个服务或端口使用了一个二进制实用控制程序或是一个用其它语言编写的例程,
如果你的某个服务或端口使用了二进制控制程序或是用其它语言编写的例程,
请将其组件安装到 <filename>/usr/sbin</filename>(相对于系统)
或 <filename>/usr/local/sbin</filename>相对于ports
然后从适的 <filename>rc.d</filename> 目录的
然后从适的 <filename>rc.d</filename> 目录的
&man.sh.1; 脚本调用它。</para>
</note>
<tip>
<para>如果你想知道为 <filename>rc.d</filename>
<para>如果你想知道为什么 <filename>rc.d</filename>
脚本必须用 &man.sh.1; 语言编写的细节,先看下
<filename>/etc/rc</filename> 是如何依靠
<function>run_rc_script</function> 调用它们,
然后再去学习 <filename>/etc/rc.subr</filename>
<function>run_rc_script</function>
下 <function>run_rc_script</function>
的相关实现。</para>
</tip>
</callout>
@ -234,23 +232,23 @@ run_rc_command "$1"<co id="rcng-dummy-runcommand"></programlisting>
<para>在 <filename>/etc/rc.subr</filename> 下,
有许多定义过的 &man.sh.1; 函数可供每个
<filename>rc.d</filename> 脚本使用。这些函数在
&man.rc.subr.8; 中有说明。尽管理论上可以完全不使用
&man.rc.subr.8; 中有说明。尽管理论上可以完全不使用
&man.rc.subr.8; 来编写一个 <filename>rc.d</filename>
脚本,但它的函数证明了它是非常方便的
并能使任务更加的简单。所以所有人在编写
脚本,但它的函数已经证明了它真的很方便
能使任务更加的简单。所以所有人在编写
<filename>rc.d</filename> 脚本时都会求助于
&man.rc.subr.8; 也不足为奇了。我们也不例外。</para>
&man.rc.subr.8; 也不足为奇了。当然我们也不例外。</para>
<para>一个 <filename>rc.d</filename> 脚本在其调用
&man.rc.subr.8; 函数之前必须先 <quote>source</quote>
<filename>/etc/rc.subr</filename>使
<quote><command>.</command></quote>包括进去),
而使 &man.sh.1; 程序有机会来获悉那些函数。
首选的样式是在脚本的最开始 source
<filename>/etc/rc.subr</filename>(用
<quote><command>.</command></quote>将其包含进去),
而使 &man.sh.1; 程序有机会来获悉那些函数。
首选风格是在脚本的最开始 source
<filename>/etc/rc.subr</filename> 文件。</para>
<note>
<para>某些有用的与联网有关的函数由另一个被包进来的文件提供,
<para>某些有用的与联网有关的函数由另一个被包进来的文件提供,
<filename>/etc/network.subr</filename> 文件。</para>
</note>
</callout>
@ -258,24 +256,24 @@ run_rc_command "$1"<co id="rcng-dummy-runcommand"></programlisting>
<callout arearefs="rcng-dummy-name">
<para><anchor id="name-var">强制的变量
<envar>name</envar> 指定我们脚本的名字。
这是 &man.rc.subr.8; 所必须的。也就是,
这是 &man.rc.subr.8; 所强调的。也就是,
每个 <filename>rc.d</filename> 脚本在调用
&man.rc.subr.8; 的函数之前必须设置
<envar>name</envar> 变量。</para>
<para>现在是时候来为我们的脚本一次性选择一个独一无二的名字了。
在编写这个脚本的时我们将在许多地方用到它。在开始之前,
我们来给脚本文件也取上一个相同的名字。</para>
在编写这个脚本的时我们将在许多地方用到它。在开始之前,
我们来给脚本文件也取个相同的名字。</para>
<note>
<para>当前的 <filename>rc.d</filename>
脚本风格是把值放在双引号中来分配给变量。
脚本风格是把值放在双引号中来给变量赋值
请记住这只是个风格问题,可能并不总是这样。
你可以在只是简单的并不包括 &man.sh.1;
元字符的词句中放心地省略掉引号,
而在某些情况下你将需要使用单引号以防止
&man.sh.1; 对任何的变量的解释。
程序员应该是能够聪明地从风格条条框框以及使用两者中来说出语言的语法的。
程序员是可以灵巧地由风格惯例获悉其语法以及使用的。
</para>
</note>
</callout>
@ -293,26 +291,26 @@ run_rc_command "$1"<co id="rcng-dummy-runcommand"></programlisting>
&man.rc.subr.8; 是如何为标准参数提供默认方法的。</para>
<note>
<para>为使得 <filename>rc.d</filename> 中的代码更加统一,
<para>为了让 <filename>rc.d</filename> 中的代码更加统一,
常见的是在任何适合的地方都使用 <envar>${name}</envar> 形式。
因此,可以轻松地将一些代码从一个脚本拷贝到另一个中使用。</para>
这样一来,可以轻松地将一些代码从一个脚本拷贝到另一个中使用。</para>
</note>
</callout>
<callout arearefs="rcng-dummy-stopcmd">
<para>我们应谨记 &man.rc.subr.8; 为标准参数提供了默认的方法。
<para>我们应谨记 &man.rc.subr.8; 为标准参数提供了默认的方法。
因此,如果希望它什么都不做的话,我们必须使用无操作的
&man.sh.1; 表达式来改写标准的方法。</para>
</callout>
<callout arearefs="rcng-dummy-startfn">
<para>比较复杂的方法主体可以用函数来实现。
在能够保证函数名有意义的情况下,这是个很不错的主意。</para>
在能够保证函数名有意义的情况下,这是个很不错的想法。</para>
<important>
<para>强烈推荐给我们脚本中所定义的所有函数名都添加类似
<envar>${name}</envar> 这样的前缀,以使它们永远不会和
&man.rc.subr.8; 或其它公用包含文件中的函数冲突。</para>
&man.rc.subr.8; 或其它公用包含文件中的函数冲突。</para>
</important>
</callout>
@ -320,13 +318,13 @@ run_rc_command "$1"<co id="rcng-dummy-runcommand"></programlisting>
<para>这是在请求 &man.rc.subr.8; 载入 &man.rc.conf.5; 变量。
尽管我们这个脚本中使用的变量并没有被其它地方使用,但由于
&man.rc.subr.8; 自身所控制着的 &man.rc.conf.5;
变量存在的缘故,仍然推荐脚本去装载 &man.rc.conf.5;。</para>
变量存在的原因,仍然推荐脚本去装载 &man.rc.conf.5;。</para>
</callout>
<callout arearefs="rcng-dummy-runcommand">
<para>通常这是 <filename>rc.d</filename> 脚本的最后一个命令。
它调用 &man.rc.subr.8;
结构使用我们脚本所提供的变量和方法来执行相应的请求动作。</para>
体系使用我们脚本所提供的变量和方法来执行相应的请求动作。</para>
</callout>
</calloutlist>
</sect1>
@ -334,14 +332,14 @@ run_rc_command "$1"<co id="rcng-dummy-runcommand"></programlisting>
<sect1 id="rcng-confdummy">
<title>可配置的虚拟脚本</title>
<para>现在我们来给我们的虚拟脚本增加一些控制参数吧。正如你所知
<para>现在我们来给我们的虚拟脚本增加一些控制参数吧。正如你所知,
<filename>rc.d</filename> 脚本是由 &man.rc.conf.5; 所控制的。
幸运的是,&man.rc.subr.8; 隐藏了我们所有复杂化的东西。
幸运的是,&man.rc.subr.8; 隐藏了所有复杂化的东西。
下面这个脚本使用 &man.rc.conf.5; 通过 &man.rc.subr.8;
来查看它是否在第一个地方被启用,并获取一条信息在启动时显示。
事实上这两个任务是相互独立的。一方面,<filename>rc.d</filename>
脚本要能够支持启动和禁用它的服务。另一方面,必须性的
<filename>rc.d</filename> 脚本要能够具备配置信息变量。
脚本要能够支持启动和禁用它的服务。另一方面,
<filename>rc.d</filename> 脚本必须能具备配置信息变量。
我们将通过下面同一脚本来演示这两方面的内容:</para>
<informalexample>
@ -372,7 +370,7 @@ run_rc_command "$1"</programlisting>
<callout arearefs="rcng-confdummy-rcvar">
<para>变量 <envar>rcvar</envar> 指定了 ON/OFF
开关变量的名字。包含来自 &man.rc.subr.8;
变量名的原因是为了调用不同操作系统所采用的不同规定的
中变量名是为了调用不同操作系统所采用的不同规定的
<function>set_rcvar</function>。也就是说,&os; 坚持使用
<envar>${name}_enable</envar> 样式而 NetBSD 的
&man.rc.conf.5; 中使用的只是 <envar>${name}</envar>
@ -382,8 +380,8 @@ run_rc_command "$1"</programlisting>
</callout>
<callout arearefs="rcng-confdummy-loadconfig">
<para>现在 <function>load_rc_config</function> 在任何
&man.rc.conf.5; 变量被访问到之前就在脚本中被提早调用。</para>
<para>现在 <function>load_rc_config</function> 在任何
&man.rc.conf.5; 变量被访问之前就在脚本中被预先调用。</para>
<note>
<para>检查 <filename>rc.d</filename> 脚本时,切记 &man.sh.1;
@ -392,7 +390,7 @@ run_rc_command "$1"</programlisting>
之前调用 <function>load_rc_config</function>
以及仍然访问从方法函数输出到
<function>run_rc_command</function> 的 &man.rc.conf.5;
变量并不是一个错误。这是因为方法函数将
变量并不是一个错误。这是因为方法函数将在
<function>load_rc_config</function> <emphasis>之后</emphasis>
被调用的 <function>run_rc_command</function> 调用。</para>
</note>
@ -400,13 +398,13 @@ run_rc_command "$1"</programlisting>
<callout arearefs="rcng-confdummy-enable">
<para>如果自身设置了 <envar>rcvar</envar>
但指示开关变量却没有被设置,那么 <function>run_rc_command</function>
发出一个警告。如果你的 <filename>rc.d</filename>
但指示开关变量却被设置,那么 <function>run_rc_command</function>
将发出一个警告。如果你的 <filename>rc.d</filename>
脚本是为基本系统所用的,你应当在
<filename>/etc/defaults/rc.conf</filename>
中给开关变量添加一个默认的设置并将其标注在 &man.rc.conf.5; 中。
否则的话你的脚本应该给开关变量提供一个默认设置。
范例中示了一个可移植接近于后者情况的案例。</para>
范例中示了一个可移植接近于后者情况的案例。</para>
<note>
<para>你可以通过将开关变量设置为 ON 来使 &man.rc.subr.8; 有效,
@ -427,7 +425,7 @@ run_rc_command "$1"</programlisting>
<callout arearefs="rcng-confdummy-opt">
<para>现在启动时显示的信息不再是硬编码在脚本中的了。
它是由一个命名为 <envar>dummy_msg</envar> 的
&man.rc.conf.5; 变量所指定的。这是 &man.rc.conf.5;
&man.rc.conf.5; 变量所指定的。这是 &man.rc.conf.5;
变量如何来控制 <filename>rc.d</filename>
脚本的一个小例子。</para>
@ -455,7 +453,7 @@ run_rc_command "$1"</programlisting>
脚本不需要为它们的 &man.rc.conf.5; 变量提供默认值,
因为默认值应该是在 <filename>/etc/defaults/rc.conf</filename>
设置过了。但另一方面,为 ports 所用的 <filename>rc.d</filename>
脚本应提供如范例所示的默认设置。</para>
脚本应提供如范例所示的默认设置。</para>
</note>
</callout>
@ -503,7 +501,7 @@ run_rc_command "$1"</programlisting>
以及 <option>status</option>。</para>
<para>该守护进程将会由运行中的 <envar>$command</envar>
配合由 <envar>$mumbled_flags</envar> 所指定命令行标帜来启动。
配合由 <envar>$mumbled_flags</envar> 所指定命令行标帜来启动。
因此,对默认的 <option>start</option> 方法来说,
所有的输入数据在我们脚本变量集合中都可用。与
<option>start</option> 不同的是,
@ -520,14 +518,14 @@ run_rc_command "$1"</programlisting>
<note>
<para>某些程序实际上是可执行的脚本。
系统启动脚本的解释器以传递脚本名为命令行参数的形式来运行脚本。
然后被映射到进程列表中,这会 &man.rc.subr.8; 迷惑。因此,当
然后被映射到进程列表中,这会使 &man.rc.subr.8; 迷惑。因此,当
<envar>$command</envar> 是一个脚本的时,你应该额外地设置
<envar>command_interpreter</envar> 来让 &man.rc.subr.8;
知晓进程的实际名字。</para>
<para>对每个 <filename>rc.d</filename> 脚本而言,
有一个可选的 &man.rc.conf.5; 变量给
<envar>command</envar> 优先级。
<envar>command</envar> 指示其优先级。
它的名字是下面这样的形式:<envar>${name}_program</envar>
<envar>name</envar> 是我们 <link linkend="name-var">之前</link>
讨论过的必须性变量。如,在这个案例中它应该命名为
@ -554,9 +552,9 @@ run_rc_command "$1"</programlisting>
<sect1 id="rcng-daemon-adv">
<title>启动并停止高级守护进程</title>
<para>我们来给之前的 <quote>骨架</quote>
<para>我们来给之前的 <quote>骨架</quote>
脚本加点 <quote>血肉</quote>,并让它更复杂更富有特性吧。
默认的方法能够为我们做很好的工作了,
默认的方法能够为我们做很好的工作了,
但是我们可能会需要它们一些方面的调整。
现在我们将学习如何调整默认方法来符合我们的需要。</para>
@ -633,14 +631,14 @@ run_rc_command "$1"</programlisting>
<envar>${name}_flags</envar> 中所列出的参数后面;
但大多的命令将不能识别出普通参数后的破折号选项。
更好的传递附加给 <envar>$command</envar>
的选项的方是添加它们到 <envar>${name}_flags</envar>
的选项的方是添加它们到 <envar>${name}_flags</envar>
的起始处。另一种方法是像后文所示的那样来修改
<envar>rc_flags</envar>。</para>
</note>
</callout>
<callout arearefs="rcng-daemon-adv-pid">
<para>一个礼貌的守护进程应该创建一个
<para>一个得体的守护进程会创建一个
<emphasis>pidfile</emphasis> 进程文件,
以使其进程能够更容易更可靠地被找到。如果设置了
<envar>pidfile</envar> 变量,告诉 &man.rc.subr.8;
@ -661,7 +659,7 @@ run_rc_command "$1"</programlisting>
&man.rc.subr.8; 将在启动守护进程之前检查那些文件是否存在。
还有相关的分别用来检查目录和环境变量的
<envar>required_dirs</envar> 和 <envar>required_vars</envar>
可供使用。它们都在 &man.rc.subr.8; 中有详细描述。</para>
可供使用。它们都在 &man.rc.subr.8; 中有详细的说明。</para>
<note>
<para>来自 &man.rc.subr.8; 的默认方法,通过使用
@ -683,17 +681,17 @@ run_rc_command "$1"</programlisting>
<para>信号名称应当以不包含 <literal>SIG</literal>
前缀的形式指定给 &man.rc.subr.8;,就如范例中所示的那样。
&os; 版本的 &man.kill.1; 程序能够识别出
<literal>SIG</literal> 前缀,不过其系统版本的就不一定了。</para>
<literal>SIG</literal> 前缀,不过其系统版本的就不一定了。</para>
</note>
</callout>
<callout arearefs="rcng-daemon-adv-precmd rcng-daemon-adv-postcmd">
<para>在默认的方法前或后面执行额外的任务是很容易的。
对于我们脚本所支持的每条命令参数而言,我们可以定义
<para>在默认的方法前面或后面执行附加任务是很容易的。
对于我们脚本所支持的每条命令参数而言,我们可以定义
<envar><replaceable>argument</replaceable>_precmd</envar> 和
<envar><replaceable>argument</replaceable>_postcmd</envar>
实现。这些 &man.sh.1; 命令分别在它们各自的方法前后被调用,
很明显,这从它们各自的名字就能够看出来。</para>
完成。这些 &man.sh.1; 命令分别在它们各自的方法前后被调用,
显然,从它们各自的名字就能看出来。</para>
<note>
<para>如果我们需要的话,用自定义的
@ -720,18 +718,18 @@ run_rc_command "$1"</programlisting>
<envar>extra_commands</envar> 中将它们列出并提供方法以处理它们。</para>
<note>
<para><option>reload</option> 是个特别的命令。一方面
它有一个在 &man.rc.subr.8; 中预置的方法。另一方面
<para><option>reload</option> 是个特别的命令。一方面,
它有一个在 &man.rc.subr.8; 中预置的方法。另一方面,
<option>reload</option> 命令默认是不被提供的。
理由是并非所有的守护进程都使用同样的重载方法,
并且有些守护进程根本没有任何东西可重载的。所以很显然
并且有些守护进程根本没有任何东西可重载的。所以显而易见
我们需要去询问都提供了哪些的内建功能。我们可以通过
<envar>extra_commands</envar> 来这样做。</para>
<para>我们从 <option>reload</option> 的默认方法得到了什么呢?
守护进程常常在收到一个信号后重新载入它们的配置 &mdash;
一般来说,也就是 <symbol>SIGHUP</symbol> 信号。因此
&man.rc.subr.8; 试发送一个信号给守护进程来重载它。
&man.rc.subr.8; 试发送一个信号给守护进程来重载它。
该信号一般预设为 <symbol>SIGHUP</symbol>
但是如果必要的话可以通过 <envar>sig_reload</envar>
变量来自定义它。</para>
@ -750,9 +748,9 @@ run_rc_command "$1"</programlisting>
通常它们是为了系统管理员的方便。它们还能被其它的子系统所使用,
例如,&man.devd.8;,前提是 &man.devd.conf.5; 中已经指定了。</para>
<para>全部可用命令的列表,当脚本不加参数地调用时,在
<para>全部可用命令的列表,当脚本不加参数地调用时,在
&man.rc.subr.8; 打印出的使用方法中能够找到。例如,
是供学习的脚本用法的内容:</para>
是供学习的脚本用法的内容:</para>
<screen>&prompt.root; <userinput>/etc/rc.d/mumbled</userinput>
Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll)</screen>
@ -763,8 +761,8 @@ Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|
这可能看起来有点像函数的调用,但我们知道,命令和 shell
函数并非一直都是同样的东西。举个例子,<command>xyzzy</command>
在这里不是以函数来实现的。另外,还有应该被有序调用的
pre-command 预命令和 post-command 后命令。
所以脚本运行自己命令的适当方法就是利用 &man.rc.subr.8;
pre-command 预命令和 post-command 后命令。
所以脚本运行自己命令的合适方式就是利用 &man.rc.subr.8;
就像范例中展示的那样。</para>
</callout>
@ -796,7 +794,7 @@ Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|
fi</programlisting>
<para>相反地,以下面的方式调用 <function>checkyesno</function>
是不会工作的??至少是不会如你预期的那样:</para>
是不会工作的 -- 至少是不会如你预期的那样:</para>
<programlisting>if checkyesno "${mumbled_enable}"; then
foo
@ -813,10 +811,10 @@ fi</programlisting>
<callout arearefs="rcng-daemon-adv-warn">
<para>某种情况下我们可能需要发出一条重要的信息,那样的话
<application>syslog</application> 可以很好地记录日志。
可以使用下列 &man.rc.subr.8; 函数来轻松完成:
这可以使用下列 &man.rc.subr.8; 函数来轻松完成:
<function>debug</function><function>info</function>
<function>warn</function>,以及 <function>err</function>。
面的函数以指定的代码值退出脚本。</para>
以指定的代码值退出脚本。</para>
</callout>
<callout arearefs="rcng-daemon-adv-preret">
@ -843,19 +841,19 @@ fi</programlisting>
(对基本系统而言)或 <filename>/usr/local/etc/rc.d</filename>
对ports而言中去。在 &lt;<filename>bsd.prog.mk</filename>&gt; 和
&lt;<filename>bsd.port.mk</filename>&gt; 中都为此提供了方便的接口,
通常你不必担心适当的所有权和模式。系统脚本应当是通过可以在
通常你不必担心适当的所有权和模式。系统脚本应当是通过可以在
<filename>src/etc/rc.d</filename> 找到的 <filename>Makefile</filename>
安装的。Port 脚本可以像
<ulink url="&url.books.porters-handbook;/rc-scripts.html">Porter's Handbook</ulink>
中描述那样通过使用 <makevar>USE_RC_SUBR</makevar> 来被安装。</para>
<para>然而,我们应该预先考虑到我们脚本在系统启动顺序中的位置。
<para>不过,我们应该预先考虑到我们脚本在系统启动顺序中的位置。
我们的脚本所处理的服务可能依赖于其它的服务。举个例子,
没有网络接口和路由选择的启用运行的话,一个网络守护进程是不起作用的。
即使一个服务看似什么都不需要,在基本文件系统检查挂载完毕之前也很难启动。</para>
<para>之前我们曾提到过 &man.rcorder.8;。现在是时候来密切地关注下它了。
概括地说,&man.rcorder.8; 处理一组文件,检验它们的内容,
笼统地说,&man.rcorder.8; 处理一组文件,检验它们的内容,
并从文件集合打印一个文件列表的依赖顺序到 <varname>stdout</varname>
标准输出。这点是用于保持文件内部的依赖信息,
而每个文件只能说明自己的依赖。一个文件可以指定如下信息:</para>
@ -877,9 +875,9 @@ fi</programlisting>
</listitem>
<listitem>
<para>附加的能用于从全部文件集合中选择一个子集的
<para>能用于从全部文件集合中选择一个子集的额外
<emphasis>关键字</emphasis> &man.rcorder.8;
能够通过选项而被指示来包括或省去由特殊关键字所列出的文件。)</para>
可通过选项而被指定来包括或省去由特殊关键字所列出的文件。)</para>
</listitem>
</itemizedlist>
@ -893,7 +891,7 @@ fi</programlisting>
并因系统管理员错误地在 &man.rc.conf.5;
中禁用掉该服务而使其不能自行启动时,会需要这一点。</para>
<para>将这些记在心,我们来考虑下简单结合了依赖信息增强的守护进程脚本:</para>
<para>将这些记在心,我们来考虑下简单结合了依赖信息增强的守护进程脚本:</para>
<informalexample>
<programlisting>#!/bin/sh
@ -950,7 +948,7 @@ run_rc_command "$1"</programlisting>
<note>
<para><literal>BEFORE:</literal>
这一行不应当滥用于其它脚本不完整的依赖关系列表中
这一行不可以在其它脚本不完整的依赖关系列表中滥用
适合使用 <literal>BEFORE:</literal>
的情况是当其它脚本不关心我们的脚本,
但是我们的脚本如果在另一个之前运行的话能够更好地执行任务。
@ -970,7 +968,7 @@ run_rc_command "$1"</programlisting>
&man.rc.8; 也不会去追踪。所以,
脚本启动的应用程序应当能够应付任何所需求的服务的不可用情况。
某些情况下,我们可以用 <link linkend="forcedep">下面</link>
所讨论的方式来助脚本。</para>
所讨论的方式来助脚本。</para>
</note>
</callout>
@ -1040,7 +1038,7 @@ run_rc_command "$1"</programlisting>
如果你的脚本管理着一个服务,那么,在系统进入 &man.halt.8;
中所描述的其本身关闭顺序的最终阶段之前停止该脚本,
可能是个不错的主意。特别是,
你显然是应该关闭一个需要相当长时间,
你显然是应该关闭一个需要相当长时间,
或需要特定的动作才能干净地关闭的服务。
数据库引擎就是这样一个典型的例子。</para>
</note>
@ -1168,9 +1166,9 @@ Greeting message: Hello world!</screen>
</callout>
<callout arearefs="rcng-args-kiss">
<para>同样用于我们脚本提供的任何方法,并不仅限于标准的方法。
<para>同样用于我们脚本提供的任何方法,并不仅限于标准的方法。
我们已经添加了一个自定义的叫做 <option>kiss</option> 的方法,
并且它给附加参数带来的戏决不亚于 <option>start</option>。
并且它给附加参数带来的戏决不亚于 <option>start</option>。
例如:</para>
<screen>&prompt.root; <userinput>/etc/rc.d/dummy kiss</userinput>
@ -1189,7 +1187,7 @@ A ghost gives you a kiss and whispers: Once I was Etaoin Shrdlu...</screen>
<para>一个 &man.sh.1; 程序员应该是可以理解
<envar>$*</envar> 和 <envar>$@</envar>
的微妙区别只是指定全部定位参数的不同方法。
关于这的更深入的探讨,可以参考这个很好的 &man.sh.1;
关于更深入的探讨,可以参考这个很好的 &man.sh.1;
脚本编程手册。在你完全理解这些表达式的意义之前请不要使用它们,
因为误用它们将给脚本引入缺陷和不安全的弊端。</para>
</important>
@ -1218,11 +1216,11 @@ A ghost gives you a kiss and whispers: Once I was Etaoin Shrdlu...</screen>
还有 &man.rcorder.8; 的联机手册中,对
<filename>rc.d</filename> 组件做了非常详细的记载。
在你写脚本时,如果不去学习和参考这些联机手册的话,
你是无法完全发挥出 <filename>rc.d</filename> 的能的。</para>
你是无法完全发挥出 <filename>rc.d</filename> 的能的。</para>
<para>工作中实际范例的主要来源就是运行的系统中的
<filename>/etc/rc.d</filename> 目录。
它的内容是非常易于阅读的,因为大部分的枯燥的内容都深藏在
它的内容可读性非常好,因为大部分的枯燥的内容都深藏在
&man.rc.subr.8; 中了。切记 <filename>/etc/rc.d</filename>
的脚本也不是神仙写出来的,
所以它们可能也存在着代码缺陷以及低级的设计方案。