9. 启动时自动启动您的软件

Linux 启动(和停止)所有子系统的方式非常简单且模块化。 它允许您定义初始化顺序、运行级别

9.1. 从 BIOS 到子系统

让我们回顾一下启动 Linux 时会发生什么

  1. BIOS 或引导加载程序(lilozlilogrub 等)从磁盘加载 Linux 内核到内存,并使用引导加载程序配置中定义的一些参数。 我们可以通过观察屏幕上出现的点来看到这个过程。 内核文件位于/boot目录中,并且仅在此刻被访问。

  2. 在内存中,内核代码开始运行,检测一系列重要的设备、磁盘分区等。

  3. 内核做的最后一件事之一是挂载/(根)文件系统,它必须包含/etc, /sbin, /bin/lib目录。

  4. 紧接着,调用名为 init 的程序(/sbin/init),并将控制权传递给它。

  5. init 命令将读取其配置文件(/etc/inittab),该文件定义了系统运行级别,以及一些要运行的 Shell 脚本。

  6. 这些脚本将继续设置系统最小的基础设施,挂载其他文件系统(根据/etc/fstab),激活交换空间(虚拟内存)等。

  7. 最后一步,也是您最感兴趣的一步,是执行名为/etc/rc.d/rc的特殊脚本,它根据/etc/rc.d下的目录结构初始化子系统。 名称 rc 来自 run commands(运行命令)。

9.2. 运行级别

运行级别机制使 Linux 能够以不同的方式初始化自身。 并且还允许我们在不重启的情况下从一个配置文件(运行级别)更改为另一个配置文件。

默认运行级别在/etc/inittab中使用如下行定义

运行级别是 0 到 6 的数字,每个运行级别都按照以下标准使用

您可以使用 telinit 命令从一个运行级别切换到另一个运行级别。 并且您可以使用 runlevel 命令查看当前运行级别和上一个运行级别。 请看下面我们如何从运行级别 3 切换到 5。

bash# runlevel
N 3
bash# telinit 5
bash# runlevel
3 5
bash# 

9.3. 子系统

子系统示例包括 Web 服务器、数据库服务器、OS 网络层等。 我们不会将面向用户的应用程序(如文本编辑器)视为子系统。

Linux 提供了一种优雅且模块化的方式来组织子系统的初始化。 需要考虑的一个重要事实是子系统之间的依赖关系。 例如,在基本网络子系统处于活动状态之前启动 Web 服务器是没有意义的。

子系统在/etc/init.d/etc/rc.d/rcN.d目录下组织

/etc/init.d

所有已安装的子系统都在此目录中放置一个控制程序,该程序是一个脚本,它遵循下面描述的简单标准。 这是此目录的简化列表

/etc/rc.d/rcN.d (N中的子系统( 是运行级别指示器)

这些目录必须仅包含指向/etc/init.d中脚本的特殊符号链接。 它的外观如下

请注意,所有链接名称都有一个前缀,以字母 K(来自 Kill,表示停用)或 S(来自 Start,表示激活)开头,以及一个 2 位数字,用于定义启动激活优先级。 在我们的示例中,HTTPd(优先级 75)在 Network(优先级 10)子系统之后启动。 防火墙子系统将在此运行级别中停用(K)。

因此,要使您的软件在启动过程中自动启动,它必须是一个子系统,我们将在下一节中看到如何做到这一点。

9.4. 将您的软件转换为子系统

您的软件文件将分布在文件系统中,但您需要提供一个简单且一致的界面,以便用户至少可以启动和停止它。 子系统架构促进了这种易用性,还提供了一种在系统初始化时自动启动的方式(非强制性)。 您只需创建您的/etc/init.d脚本,并遵循标准使其正常工作。

示例 6. 中的子系统控制程序框架/etc/init.d

#!/bin/sh
#
# /etc/init.d/mysystem
# Subsystem file for "MySystem" server
#
# chkconfig: 2345 95 05	(1)
# description: MySystem server daemon
#
# processname: MySystem
# config: /etc/MySystem/mySystem.conf
# config: /etc/sysconfig/mySystem
# pidfile: /var/run/MySystem.pid

# source function library
. /etc/rc.d/init.d/functions

# pull in sysconfig settings
[ -f /etc/sysconfig/mySystem ] && . /etc/sysconfig/mySystem	(2)

RETVAL=0
prog="MySystem"
.
.	(3)
.

start() {	(4)
	echo -n $"Starting $prog:"
	.
	.	(5)
	.
	RETVAL=$?
	[ "$RETVAL" = 0 ] && touch /var/lock/subsys/$prog
	echo
}

stop() {	(6)
	echo -n $"Stopping $prog:"
	.
	.	(7)
	.
	killproc $prog -TERM
	RETVAL=$?
	[ "$RETVAL" = 0 ] && rm -f /var/lock/subsys/$prog
	echo
}

reload() {	(8)
	echo -n $"Reloading $prog:"
	killproc $prog -HUP
	RETVAL=$?
	echo
}

case "$1" in	(9)
	start)
		start
		;;
	stop)
		stop
		;;
	restart)
		stop
		start
		;;
	reload)
		reload
		;;
	condrestart)
		if [ -f /var/lock/subsys/$prog ] ; then
			stop
			# avoid race
			sleep 3
			start
		fi
		;;
	status)
		status $prog
		RETVAL=$?
		;;
	*)	(10)
		echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}"
		RETVAL=1
esac
exit $RETVAL
(1)
尽管这些是注释,但它们被 chkconfig 命令使用,并且必须存在。 此特定行定义,在运行级别 2、3、4 和 5 上,此子系统将以优先级 95(最后之一)激活,并以优先级 05(最先之一)停用。
(2)
除了您的软件自身的配置之外,此脚本还可以具有配置文件。 它的标准位置在/etc/sysconfig目录下,在我们的例子中,我们称之为mySystem。 此代码行读取此配置文件。
(4)(6)(8)
您的脚本可以有很多功能,但必须实现 startstop 方法,因为它们负责在启动时(停用)激活您的子系统。 其他方法可以从命令行调用,您可以根据需要定义任意数量的方法。
(9)
定义脚本操作后,将分析命令行并调用请求的方法(操作)。
(10)
如果执行此脚本时没有任何参数,它将返回如下帮助消息
bash# /etc/init.d/mysystem
Usage: mysystem {start|stop|restart|reload|condrestart|status}
(3)(5)(7)
在这里,您放置软件的特定命令。

您实现的 mysystem 子系统方法将由用户使用 service 命令调用,如以下示例所示

您不必担心管理/etc/rc.d/rcN.d中的符号链接。 chkconfig 命令会根据脚本开头定义的控制注释为您完成此操作。

阅读 chkconfig 手册页,了解它可以为您做些什么。

9.5. 打包您的启动脚本

当您创建 RPM 时,将您的子系统脚本放在/etc/init.d中,并且不要包含任何/etc/rc.d/rcN.d链接,因为是否使您的子系统自动启动是用户的决定。 如果您包含它们并且用户进行任何更改,则 RPM 文件清单将变得不一致。

符号链接必须在您的软件包的安装后和卸载前过程中使用 chkconfig 命令动态创建和删除。 这种方法保证了 100% 的软件包和文件系统一致性。