试析FreeBSD 6.2 的rc脚本系统

杜比环绕声
http://blog.chinaunix.net/u/12258/showart_292352.html

一、从mysql的启动脚本说起

配置mysql的时候,如果需要启动运行 mysql server,按照 mysql 手册上的说明,需要在 /etc/rc.conf 中,添加一条信息:

mysql_enable=”YES”

这样在重新启动系统的时候,就可以自动运行 mysqlserver.

如果需要运行时,停止 mysqlserver 或者重新启动 mysqlserver,可以使用下面的命令:

停止mysqlserver: shell>/usr/local/etc/rc.d/mysql-server stop
重新启动mysqlserver: shell>/usr/local/etc/rc.d/mysql-server restart

其实无论是mysqlserver还是apache等系统服务,都可以利用上面提到的参数“stop“,”restart“等进行程序维护

这属于FreeBSD系统rc脚本系统的具体应用。

FreeBSD的rc脚本系统在服务程序的管理上,主要是体现在 /etc/rc.d 和 /usr/local/etc/rc.d 这两个目录下的可执行脚本,系统级别的服务程序的脚本大都安装在 /etc/rc.d目录下,而用户级别的服务程序的脚本都安装在 /usr/local/etc/rc.d 目录下。如 mysql server 在安装的时候会在 /usr/local/etc/rc.d目录下安装一个 mysql-server 的脚本文件。

服务程序的管理,其实运行的就是对应的脚本文件。如上面举例的停止服务,重新启动服务,运行的都是mysql-server脚本。

二、mysql-server 启动脚本的说明

下面的脚本代码是freebsd 6.2中mysqlserver 5.0的启动脚本。具体的功能在脚本代码中注释!

#!/bin/sh
#
# $FreeBSD: ports/databases/mysql50-server/files/mysql-server.sh.in,v 1.3 2006/03/07 16:25:00 ale Exp $
#

#下面这部分文本描述了可以在rc.conf中设置、添加的启动条目,用来控制mysqlserver启动的一些具体细节。

# PROVIDE: mysql
# REQUIRE: LOGIN
# KEYWORD: shutdown

#
# Add the following line to /etc/rc.conf to enable mysql:
# mysql_enable (bool): Set to “NO” by default.
# Set it to “YES” to enable MySQL.
# mysql_limits (bool): Set to “NO” by default.
# Set it to yes to run `limits -e -U mysql`
# just before mysql starts.
# mysql_dbdir (str): Default to “/var/db/mysql”
# Base database directory.
# mysql_args (str): Custom additional arguments to be passed
# to mysqld_safe (default empty).
#

# 下面的这行代码非常重要,作用是在mysql-server的脚本代码中插入 /etc/rc.subr 文件的代码!
# 在 /etc/rc.subr 文件中,提供了 mysql-server 调用的函数的实现,也提供了很多rc脚本系统中用到的函数。
# mysql-server 中能够使用的 start,stop,restart,rcvar等参数的具体运作,也是在 /etc/rc.subr 中提供的。

. /etc/rc.subr

# 在 /etc/rc.subr 中,首先运行一些初始化命令,跟踪运行,执行了以下命令:

if [ -z “${_rc_subr_loaded}” ]; then

_rc_subr_loaded=”YES”

SYSCTL=”/sbin/sysctl”
SYSCTL_N=”${SYSCTL} -n”
CMD_OSTYPE=”${SYSCTL_N} kern.ostype”
OSTYPE=`${CMD_OSTYPE}`
ID=”/usr/bin/id”
JID=`ps -p $$ -o jid=`
IDCMD=”if [ -x $ID ]; then $ID -un; fi”

case ${OSTYPE} in
FreeBSD)
SYSCTL_W=”${SYSCTL}”
;;
NetBSD)
SYSCTL_W=”${SYSCTL} -w”
;;
esac

# 定义name变量
name=”mysql”

# 调用 /etc/rc.subr 文件中定义的 set_rcvar 函数
# 函数的执行结果是给几个变量赋值
rcvar=`set_rcvar`

# set_rcvar的执行结果

base_var=mysql
echo mysql_enable
rcvar=mysql_enable

# 调用 /etc/rc.subr 文件中定义的 load_rc_config 函数,参数是 name 变量的值。
# 这个函数会装载 /etc/defaults/rc.conf文件的内容,并且运行它,之后运行 /etc/rc.conf
# 的内容,具体的作用就是按照这两个rc.conf的内容设置必要的变量,通过加载运行的顺序上看,因为 /etc/rc.conf运行在后
# 所以 /etc/rc.conf 定义的变量会覆盖 /etc/defaults/rc.conf中定义的变量。

load_rc_config $name

# 定义一些变量
: ${mysql_enable=”NO”}
: ${mysql_limits=”NO”}
: ${mysql_dbdir=”/var/db/mysql”}
: ${mysql_args=””}

# 定义一些变量,通过这些变量,能够组合出mysqlserver启动的具体参数。涉及mysqlserver的一些具体工具的使用。

mysql_user=”mysql” # 启动用帐户
mysql_limits_args=”-e -U ${mysql_user}” # 启动参数
pidfile=”${mysql_dbdir}/`/bin/hostname`.pid”
command=”/usr/local/bin/mysqld_safe” # 启动命令
command_args=”–defaults-extra-file=${mysql_dbdir}/my.cnf –user=${mysql_user} –datadir=${mysql_dbdir} –pid-file=${pidfile} ${mysql_args} > /dev/null &”
procname=”/usr/local/libexec/mysqld”

start_precmd=”${name}_prestart” # 定义一个函数接口,关联到本脚本文件中定义的 mysql_prestart()函数

mysql_install_db=”/usr/local/bin/mysql_install_db”
mysql_install_db_args=”–ldata=${mysql_dbdir}”

# 下面这个函数用来建立mysql数据苦,初始化授权表,并且建立一个test数据库
mysql_create_auth_tables()
{
eval $mysql_install_db $mysql_install_db_args >/dev/null
[ $? -eq 0 ] && chown -R ${mysql_user}:${mysql_user} ${mysql_dbdir}
}

# 下面这个函数用来定义程序启动前的一些动作。
# 检查mysql数据库目录是否存在,如果没有存在,运行建立数据库,初始化授权表。

mysql_prestart()
{
if [ ! -d “${mysql_dbdir}/mysql/.” ]; then
mysql_create_auth_tables || return 1
fi
if checkyesno mysql_limits; then
eval `/usr/bin/limits ${mysql_limits_args}` 2>/dev/null
else
return 0
fi
}

# 下面的一行代码是运行 /etc/rc.subr 中的 run_rc_command 函数
# 如果我们执行 /usr/local/etc/rc.d/mysql-server start
# 那么执行的就是 run_rc_command “start”
run_rc_command “$1”

######## mysql-server 脚本到此结束

余下运行的都是根据本脚本运行过程中定义的各种变量来执行 /etc/rc.subr 中的run_rc_command 函数。

三、/etc/rc.subr中的run_rc_command 函数

这个函数中提供了很多参数的处理过程,诸如start,stop,restart,rcvar,pull等过程。

也提供了可选参数fast,force,one的具体执行代码。具体的代码就不作解释了,

如果这些参数的处理过程没有在服务程序的脚本中进行定义,那么运行的就是函数中定义的处理过程。

通过脚本分析获知使用rc脚本系统中一些细节。

1、如果运行具体服务程序的rc脚本没有带参数,rc_rc_command会打印出帮助信息

Usage: ./mysql-server [fast|force|one](start|stop|restart|rcvar|status|poll)

2、对于mysql-server这个脚本,如果没有在 /etc/rc.conf 中加入 mysql_enable=”YES”,并且在运行脚本时
没有使用[force][one]选项,start,stop,restart,status是没有运行结果的。

[force]的作用是强制 {rcvar} 有效,使得 mysql_enable=”yes”临时有效

这样在 /etc/rc.conf 中不添加 mysql_enable=”YES”的情况下,运行服务程序的管理,例如:

shell> /usr/local/etc/rc.d/mysql-server forcestart
shell> /usr/local/etc/rc.d/mysql-server forcerestart

rcvar参数是不需要上面的限制,因为rcvar参数的主要作用就是判断/etc/rc.conf 中是否有mysql_enable=”YES”的设置。

[one]的作用和[force]类似,不同的是one 只是设置 {$rcvar}有效。

四、阶段性小结

通过上面的代码分析,以及参考更多的服务程序的rc脚本,大致可以理出rc脚本系统中服务程序管理的大致原理。

freebsd系统提供的 rc.subr 是整个rc脚本系统的核心,它提供了系统利用rc脚本管理服务程序的基石。

在这个基础上,实现了服务程序rc脚本的框架结构。定义了统一的运行规则。并且为服务程序诸如mysqlserver提供了很大的灵活性。

但rc.subr的作用不只如此,从rc.subr的代码可知,它提供的功能实际是架构起了FreeBSD整个系统的rc脚本系统。

五、系统启动自动运行服务程序的机制

前面提到,mysqlserver要实现随系统启动运行所要作的工作就是把 mysql_enable=”YES”添加到 /etc/rc.conf之中即可。泛泛的理解是系统启动完内核后,在启动init之后有个rc脚本启动过程。而这些服务程序就是在这个过程中自动加载的。但具体的加载过程很少资料提到。本文在上面对mysql-server分析的基础上,试着分析一下rc脚本系统的启动原理。

1、rc脚本系统什么时候启动

按照《FreeBSD操作系统设计与实现》中的说法,rc脚本系统是在加载完系统内核,出现freebsd启动模式选择界面,选择运行方式,启动init之后开始运行。在多用户模式启动系统的情况下,init首先产生一个shell,默认的是 /bin/sh,所有的rc脚本都是用这个shell来解释执行。

选择不同的启动模式会导致下面的变量被赋值:

_boot

如果系统是自动运行,_boot=faststart

如果系统不是自动运行,_boot=start

这个变量的值会影响到后续的启动过程

2、rc脚本系统的始祖 /etc/rc

# 初始化设置
stty status ‘^T’

# Set shell to ignore SIGINT (2), but not children;
# shell catches SIGQUIT (3) and returns to single user.
#
trap : 2
trap “echo ‘Boot interrupted’; exit 1” 3

HOME=/
PATH=/sbin:/bin:/usr/sbin:/usr/bin
export HOME PATH

# 设置 _boot 变量的值
if [ “$1″ = autoboot ]; then
autoboot=yes
_boot=”faststart”
rc_fast=yes # run_rc_command(): do fast booting
else
autoboot=no
_boot=”start”
fi

dlv=`/sbin/sysctl -n vfs.nfs.diskless_valid 2> /dev/null`
if [ ${dlv:=0} -ne 0 -o -f /etc/diskless ]; then
sh /etc/rc.initdiskless
fi

# Run these after determining whether we are booting diskless in order
# to minimize the number of files that are needed on a diskless system,
# and to make the configuration file variables available to rc itself.

# 包含 /etc/rc.subr,又见到“rc.subr“的踪迹 :-)
. /etc/rc.subr

# 显示提示信息,表示开始启动系统rc脚本
echo “Loading configuration files.”

# 调用 /etc/rc.subr 中的 load_rc_confg 参数是 XXX
# 调用 /etc/rc.subr 文件中定义的 load_rc_config 函数,参数是 name 变量的值。
# 这个函数会装载 /etc/defaults/rc.conf文件的内容,并且运行它,之后运行 /etc/rc.conf
# 的内容,具体的作用就是按照这两个rc.conf的内容设置必要的变量,通过加载运行的顺序上看,因为 /etc/rc.conf运行在后
# 所以 /etc/rc.conf 定义的变量会覆盖 /etc/defaults/rc.conf中定义的变量。
load_rc_config ‘XXX’

skip=”-s nostart”

# jailed相关,目前还不太了解
if [ `/sbin/sysctl -n security.jail.jailed` -eq 1 ]; then
skip=”$skip -s nojail”
if [ “$early_late_divider” = “mountcritlocal” ]; then
early_late_divider=NETWORKING
fi
fi

# Do a first pass to get everything up to $early_late_divider so that
# we can do a second pass that includes $local_startup directories
#

# 用rcorder这个工具程序按照/etc/defaults/rc.conf /etc/rc.conf中的变量定义访问 /etc/rc.d/目录下的所需文件
# rcorder根据这些脚本文件的关联进行排序,为后面的循环加载 /etc/rc.d/目录下脚本文件作准备
files=`rcorder ${skip} /etc/rc.d/* 2>/dev/null`

# 进入一个循环,对排序后的脚本文件,使用 _boot 变量的值(start,faststart)运行每一个rc脚本
# 跟踪这个循环,可知服务程序的加载顺序。
for _rc_elem in ${files}; do
run_rc_script ${_rc_elem} ${_boot}

case “$_rc_elem” in
*/${early_late_divider}) break ;;
esac
done

unset files local_rc

# Now that disks are mounted, for each dir in $local_startup
# search for init scripts that use the new rc.d semantics.
# 当分区加载成功后,搜索 /usr/local/etc/rc.d目录下的文件,根据 /etc/rc.conf中的具体设置,运行对应的脚本文件。
# 参数是 _boot变量的值(start,faststart)

case ${local_startup} in
[Nn][Oo] | ”) ;;
*) find_local_scripts_new ;;
esac

files=`rcorder ${skip} /etc/rc.d/* ${local_rc} 2>/dev/null`
_skip_early=1
for _rc_elem in ${files}; do
case “$_skip_early” in
1) case “$_rc_elem” in
*/${early_late_divider}) _skip_early=0 ;;
esac
continue
;;
esac

run_rc_script ${_rc_elem} ${_boot}
done

echo ”
date
exit 0

3、小结:

FreeBSD系统提供的rc脚本系统由以下的部分组成

shell
/etc/rc
/etc/defaults/rc.conf
/etc/rc.conf
/etc/rc.d/
/usr/local/etc/rc.d/

整个rc脚本系统的核心是 /etc/rc.subr 和 rcorder 组成,籍由这个机制,很多的系统功能都是通过rc脚本运行起来的,例如 login 等等

《freebsd操作系统设计与实现》中文版中所提到的reorder应该就是rcorder,估计是排版错误。

系统启动时自动运行特定的服务程序的大致流程是:

由 /etc/rc 脚本运行 /etc/rc.conf ,对应的 mysql_enable=”YES”所起的作用就是设置rcvar变量

通过/etc/rc.subr提供的函数,使用 faststart或者start参数运行诸如 mysql-server这样的脚本,启动相关服务

六、FreeBSD的rc脚本系统总结

通过跟踪整个rc脚本系统的运作,给我一个特殊的印象,一个不太恰当的比喻,rc脚本系统相当于系统内核和具体服务程序的黏合剂和管理中枢。有了rc脚本系统,磁盘检查,用户登录等等服务才得以在系统中运行。

发表评论

电子邮件地址不会被公开。 必填项已用*标注