杜比环绕声 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

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脚本系统,磁盘检查,用户登录等等服务才得以在系统中运行。