# BSD rc.d脚本编程实战

### Yar Tikhiy

`<yar@FreeBSD.org>`

$FreeBSD: release/8.4.0/zh_CN.GB2312/articles/rc-scripting/article.xml 39632 2012-10-01 11:56:00Z gabor $

版权 © 2005, 2006, 2012 The FreeBSD Project

$FreeBSD: release/8.4.0/zh_CN.GB2312/articles/rc-scripting/article.xml 39632 2012-10-01 11:56:00Z gabor $

初学者可能会发现，难以通过正式的文档， 基于 BSD 的 `rc.d` 框架，编写一些实际任务的 `rc.d` 脚本。 本文中，我们采用了一些复杂性不断增加的典型案例， 来展示适合每个案例的 `rc.d` 特性， 并探讨其中的工作原理。 这样的实验为大家进一步研究设计有效的 `rc.d` 应用程序提供了一些参考点。

# 1 简介

历史上 BSD 曾有过一个单一的启动脚本， `/etc/rc`。 该脚本在系统启动的时候被 init(8) 程序所引导，并执行所有多用户操作所需求的用户级任务： 检查并挂载文件系统，设置网络，启动守护进程，等等。 在每个系统中实际的任务清单也并不相同； 管理员需要根据需求自定义这样的任务清单。在一些特殊的情况中， 还不得不去修改 `/etc/rc` 文件， 一些真正的黑客乐此不疲。

单一脚本启动方法的真正问题是它没有提供对从 `/etc/rc` 启动的单个组件的控制。 拿一个例子来说吧，`/etc/rc` 不能够重新启动某个单独的守护进程。 系统管理员不得不手动找出守护进程，并杀掉它， 等待它真正退出后，再通过浏览 `/etc/rc` 得到该守护进程的标识，最终输入全部命令来再次启动守护进程。 如果重新启动的服务包括不止一个守护进程或需要更多动作的话， 该任务将变得更加困难以及容易出错。简而言之， 单一脚本在实现我们这样的目的上是不成功的： 让系统管理员的生活更轻松。

再后来，为了将最重要的一些子系统独立出来， 便尝试将部分的内容从 `/etc/rc` 分离出来了。 最广为人知的例子就是用来启动联网的 `/etc/netstart` 文件。它容许从单用户模式访问网络， 但由于它的部分代码需要和一些与联网完全无关的动作交互， 所以它并没有完美地结合到自启动的进程中。那便是为何 `/etc/netstart` 被演变成 `/etc/rc.network` 的原因了。 后者不再是一个普通的脚本；它包括了庞大的，由 `/etc/rc` 在不同的系统启动级别中调用的凌乱的 sh(1) 函数。然而，当启动任务变得多样化以及久经更改， “类模块化” 方法变得比曾经的整体 `/etc/rc` 更缓慢费事。

由于没有一个干净和易于设计的框架， 启动脚本不得不全力更改以满足飞速开发中基于 BSD 的操作系统的需求。 它逐渐变得明朗并经过许多必要的步骤最终变成一个具有细密性和扩展性的 `rc` 系统。BSD `rc.d` 就这样诞生了。Luke Mewburn 和 NetBSD 社区是公认的 `rc.d` 之父。再之后它被引入到了 FreeBSD 中。 它的名字引用为系统单独的服务脚本的位置，也就是 `/etc/rc.d`下面的那些脚本。 之后我们将学习到更多的 `rc.d` 系统的组件并看看单个脚本是如何被调用的。

BSD `rc.d` 背后的基本理念是 良好 的模块化和代码重用性。 良好 的模块化意味着每个基本 “服务” 就象系统守护进程或原始启动任务那样， 通过属于它们的可启动该服务的 sh(1) 脚本，来停止服务， 重载服务，检查服务的状态。具体动作由脚本的命令行参数所决定。 `/etc/rc` 脚本仍然掌管着系统的启动， 但现在它仅仅是使用 `start` 参数来一个个调用那些小的脚本。 这便于用 `stop` 来对运行中的同样的脚本很好地执行停止任务， 这是被 `/etc/rc.shutdown` 脚本所完成的。看，这是多么好地体现了 Unix 的哲学： 拥有一组小的专用的工具，每个工具尽可能好地完成自己的任务。 代码重用 意味着所有的通用操作由 `/etc/rc.subr` 中的一些 sh(1) 函数所实现。 现在一个典型的脚本只需要寥寥几行的 sh(1) 代码。最终， rcorder(8) 成为了 `rc.d` 框架中重要的一部分， 它用来帮助 `/etc/rc` 处理小脚本之间的依赖关系并有次序地运行它们。它同样帮助 `/etc/rc.shutdown` 做类似的事情， 因为正确的关闭次序是相对于启动的次序的。

BSD `rc.d` 的设计在 Luke Mewburn 的原文 中有记录， 以及 `rc.d` 组件也被充分详细地记录在各自的 联机手册 中。然而， 它可能没能清晰展现给一个 `rc.d` 新手，如何将无数的块和片进行关联来为具体的任务创建一个好风格的脚本。 因此本文将试着以不同的方式来讲述 `rc.d`。 它将展示在某些典型情况中应该使用哪些特性，并阐述了为何如此。 注意这并不是一篇 how-to 文档，我们的目的不是给出现成的配方， 而是在展示一些简单的进入 `rc.d` 的范围的门路。 本文也不是相关联机手册的替代品。 阅读本文时记得同时参考联机手册以获取更完整正规的文档。

理解本文需要一些先决条件。首先，你需要熟悉 sh(1) 脚本编程语言以掌握 `rc.d`， 还有，你需要知道系统是如何执行用户级的启动和停止任务，这些在 rc(8) 中都有说明。

本文关注的是 `rc.d` 的 FreeBSD 分支。 不过，它可能对 NetBSD 的开发者也同样有用，因为 BSD `rc.d` 的两个分支不只是共享了同样的设计， 还保留了对脚本编写者都可见的类似观点。

|  | 下一页
|  | 任务描述

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

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