在这篇关于配置 Linux 服务在重启或系统崩溃后自动启动的两部分教程的第二部分中,我们将详细讨论 init 系统。您可以参考 该系列的第一部分:如何配置 Linux 服务在重启或系统崩溃后自动启动:此处为实际示例.
本教程将偏重于理论。因此,您应该将其作为参考,以深入了解 Linux 中 init 系统的工作原理。在本教程的第一部分中,我们分享了 init 系统在启动时读取的一些代码片段和启动脚本。我们还使用了 MySQL 作为示例,学习了如何启用和禁用 Linux 服务以在崩溃或重启后自动启动。正如您在这篇两部分教程的第一部分中所学到的,在不同的 Linux 发行版中使用了三种 init 系统:System V、Upstart 和 Systemd。您可以参考 本教程的第一部分,以了解配置为使用特定 init 系统的发行版和版本.
在本教程中,我们将解释我们在教程第一部分中使用的代码。我们将详细阐述 init 系统使用的命令和配置文件。让我们开始吧!
前提条件
在这篇两部分教程的第一部分结束时,我们提到您应该保持这三台测试服务器运行。如果您已经删除了它们,可以返回并重新创建它们。这将有助于您跟上教程的进度。您应该拥有的三台测试服务器是:
- Ubuntu 9.04 及更早版本,或 Debian 6 x64(我们将使用它来演示 System V init 系统)
- Ubuntu 14.04 x64(我们将使用它来演示 Upstart)。这里有一个 关于如何轻松设置 Ubuntu 服务器的教程.
- CentOS 7 x64(我们将使用它来演示 Systemd)。
在您将用于运行命令的每台服务器上,您应该拥有一个具有 sudo 特权的用户。这篇 关于配置 Linux sudoers 文件的教程可以为您提供指导.
注意: 教程中的命令会干扰系统服务。因此,您不应在生产服务器上应用它们。
运行级别
一个 运行级别 是一个操作级别,描述了 Linux 系统当前状态下可用的服务。该概念起源于 System V init。当 Linux 系统启动时,它会初始化内核,进入一个运行级别,并运行与该运行级别关联的启动脚本。您在启动时只能执行一个运行级别。
运行级别的其他示例包括关机状态、重启模式、单用户模式等。每个级别决定了在该状态下运行哪些服务。某些服务可以在多个级别上运行,而其他服务则不能。
共有七个运行级别:从 0 到 6。以下是这七个运行级别的定义:
- 运行级别 0:系统关机
- 运行级别 1:单用户和救援模式
- 运行级别 2、3、4:启用网络的多用户和文本模式
- 运行级别 5:多用户、启用网络和图形模式
- 运行级别 6:系统重启
运行级别不一定按顺序执行。运行级别 2、3 和 4 因您运行的 Linux 发行版而异。您可以在某些发行版中实现运行级别 4,而在其他发行版中则不行。当您启用服务自动启动时,实际上是将其添加到了一个运行级别中。在 System V 中,操作系统以特定的运行级别启动,并在启动过程中尝试启动与该运行级别关联的所有服务。在 Systemd 中,运行级别被称为目标(targets),我们将在 Systemd 部分中进行讨论。
Init 和 PID 1
Init 系统是 Linux 系统启动且内核加载到内存时运行的第一个进程。它执行各种任务,包括确定用户进程或系统服务将如何加载、以何种顺序加载以及是否应自动启动。在每个 Linux 发行版中,每个进程都由进程 ID (PID) 标识,而 init 的 PID 为 1。它是系统启动时陆续启动的所有其他进程的父进程。
init 的历史
在最近的 Linux 发行版中发现的 init 系统是对原始系统的改进。最早版本的 Linux 发行版使用的是 System V init,它同样被用于 Unix 系统。随着 Linux 的演变,Upstart init 守护进程被实现——它是由 Ubuntu。现在(在编写本教程时,即 2021 年),我们有了 Systemd init 守护进程——它首先由 Fedora。随着 Linux 系统的不断演变,可能会出现更新的 init 系统。在本教程中,我们将讨论这三个系统:System V、Upstart 和 Systemd。
最近的 Linux 版本默认带有 Systemd init 系统。然而,为了向后兼容,它们保留了其他较旧的 init 系统。您可以在其他 Linux 变体中使用 System V 的不同实现。例如,UNIX 的变体 FreeBSD 使用 BSD init。较旧版本的 Debian 使用 SysVinit。两者都源自 System V。
每个版本的 init 守护进程管理服务的方式都是不同的。每个版本中增加的改进都旨在提供一个强大的服务管理工具,该工具可以处理 Linux 系统所需的一切,包括服务、设备、端口和其他资源。当时需要一个能够并行加载资源并能从系统崩溃中优雅恢复的强大系统。
System V Init 序列
System V 利用 inittab 文件来保存初始化指令。Upstart 保留了 inittab 文件以实现向后兼容。以下是 System V 的启动流程:
- init 系统来自二进制文件 /sbin/init.
- 一旦 init 系统加载到内存中,它就会读取其位于 /etc/inittab.
- 该文件中的其中一个条目决定了机器应该启动进入的运行级别。例如,如果运行级别的值指定为 5, Linux 将在启用网络的多用户图形模式下启动(这在专为桌面使用设计的发行版中很常见)。这里指定的运行级别被称为默认运行级别,因为它是将一直使用的级别。
- 然后,init 系统会进一步查看 /etc/inittab 文件,并读取该运行级别需要运行哪些 init 脚本。
通过查找针对给定运行级别要运行哪些脚本,init 系统将找到它需要启动哪些服务。这些 init 脚本通常是您为单个服务配置启动行为的地方,就像我们在本教程第一部分中配置 MySQL 服务一样。
System V Init 脚本结构
在本节中,我们将详细查看 init 脚本。System V 配置文件或 init 脚本是控制服务的内容。Init 脚本 初始化 一个服务,因此得名 init 脚本。
每个服务都有自己的 init 脚本。例如,MySQL init 脚本控制 MySQL 服务器。应用程序供应商为其特定应用程序提供 init 脚本,而原生 Linux 服务则随操作系统安装一起提供。当您创建自定义应用程序时,您也可以为其创建自己的自定义 init 脚本。
要启动像 MySQL 服务器这样的服务,首先将其二进制程序加载到内存中。根据其配置,该程序可能会继续在后台执行以继续接受客户端连接。该服务的 init 脚本负责启动、停止或重新加载二进制应用程序。System V 中的 init 脚本是 shell 脚本。它们的另一个名称是 rc(运行命令)脚本。
System V 目录结构
init 脚本的父目录是 /etc 目录。而 /etc/init.d 目录是 init shell 脚本的实际目录。这些脚本被符号链接到 rc 目录。
在 /etc 目录下有几个 rc 目录,每个目录的名称中都有一个数字。这些数字代表不同的运行级别。如果您列出该目录的内容,您将看到类似于 /etc/rc0.d, /etc/rc1.d, /etc/rc2.d 等名称。
如果您查看每个 rc 目录的内容,您将看到开头为 K 或 S 的文件 在它们的文件名中,后跟两位数字。这些文件包含指向实际程序的实际 init shell 脚本的符号链接。字母 K 和 S 是缩写:K 表示 Kill 或 Stop,而 S 代表 Start。文件名中的两位数字表示执行顺序。如果您看到一个名为 K05script_name 的文件,它将在名为 K09script_name.
启动
随着启动顺序的进行,让我们看看 init 脚本是如何被调用的。
S 和 K 脚本不是由 init 系统直接调用的。相反,它们是由另一个脚本调用的:即 /etc/init.d/rc 脚本。 /etc/inittab 文件指示 init 守护进程系统默认应该在哪个运行级别启动。根据指定的运行级别,/etc/inittab 文件中的一行会调用 /etc/init.d/rc 脚本,并将默认运行级别作为参数传递。使用此参数,该脚本将调用对应 /etc/rcN.d 目录下的文件,其中 N 表示运行级别。例如,如果服务器以运行级别 5 启动,则会调用 /etc/rc5.d 目录下的相应文件。
在 rc 目录下,所有 K 脚本都按数字顺序运行,并带有参数 stop,而所有 S 脚本也以类似方式运行,并带有 start 参数。/etc/rcN.d 符号链接指向的程序的相应实际 init shell 脚本将分别使用 stop 和 start 参数进行调用。
简单来说,每当 Linux 系统进入或切换到某个运行级别时,就会运行某些脚本来停止某些服务,同时运行其他脚本来启动其他服务。此过程可确保停止在给定 Linux 状态下不应运行的任何进程,并自动启动应该运行的任何进程。
System V 自动启动
当您启用服务在引导时自动启动时,您是在直接修改 init 行为。例如,如果您将服务配置为在运行级别 2 自动启动,则 init 进程会在 /etc/rc2.d 目录中创建相应的符号链接。为了帮助您理解,我们将通过一个示例进行解释。
System V 示例
为了给您一个实际的例子,我们将使用第 1 部分中的 MySQL 服务配置。因此,使用 ssh(如果您使用的是 Windows,则使用 putty)以 sudo/root 用户登录 Debian 6 VPS,并继续执行以下步骤。
步骤 1:打开并检查 inittab 文件
首先,在终端输入以下命令查看 inittab 文件的内容:
|
1 |
cat /etc/inittab | grep initdefault |
该文件的内容应该类似于:
|
1 |
id:2:initdefault: |
数字 2 表示系统启动时的运行级别。在这种情况下,运行级别 2 是默认值,因此该 Debian 系统将在运行级别 2 下以多用户、文本模式启动。您可以运行以下命令进行确认:
|
1 |
cat /etc/inittab | grep Runlevel |
它将显示类似于以下内容的输出:
|
1 2 3 4 |
# Runlevel 0 is halt. # Runlevel 1 is single-user. # Runlevels 2-5 are multi-user. # Runlevel 6 is reboot. |
步骤 2:检查 rc 目录
接下来,要列出 rc 目录,请运行以下命令:
|
1 |
ls -ld /etc/rc*.d |
这是输出的屏幕截图:

正如我们之前在 inittab 文件中看到的那样,系统配置为在 runlevel 2 下启动,因此 /etc/rc2.d 下的脚本将在系统启动时执行。您可以使用以下命令列出此目录的内容:
|
1 |
ls -l /etc/rc2.d |
从输出中可以看出,这些文件只是指向 /etc/init.d 下实际脚本文件的符号链接。以下是输出的片段:

此目录中没有 K 脚本,只有 S(启动)脚本。这些脚本将启动此处链接的服务,例如 rsync。您还可以注意到列出的 mysql 服务,我们将在下一个子主题中讨论它。
步骤 3:检查 Init 脚本
当安装了符合 System V 标准的服务时,它会在 /etc/init.d 目录下创建一个 shell 脚本。您可以通过输入以下命令来检查 MySQL shell 脚本是否可用:
|
1 |
ls -l /etc/init.d/my* |
它显示以下输出:

该文件相当大。您可以输入以下命令来查看其内容:
|
1 |
cat /etc/init.d/mysql | less |
步骤 4:使用 chkconfig 或 sysv-rc-conf
Chkconfig 是一个您可以在基于 RHEL 的发行版(如 CentOS)中使用的命令,用于启用或禁用 System V 兼容服务。您还可以使用它来列出已安装的服务及其各自的运行级别。以下是该命令(适用于 CentOS):
|
1 |
chkconfig --list | grep service_name |
在 Debian 发行版中,原生不存在此类实用程序。Debian 系统中的 update-rc.d 仅从运行级别中安装和删除服务。有一个自定义工具可用于将 chkconfig 功能引入 Debian 系统。输入以下命令进行安装:
|
1 |
sudo apt-get install sysv-rc-conf –y |
安装完成后,您可以运行以下命令来查看各种服务的运行级别行为:
|
1 |
sudo sysv-rc-conf |
该命令的输出格式化为一个表格,左侧显示服务名称,右侧显示该服务运行的运行级别:

X 表示服务将在其下运行的运行级别。该工具允许您使用方向键和空格键来禁用或启用某个运行级别的服务。要退出该工具,请按 Q。
步骤 5:测试系统启动期间的 MySQL 启动行为
从上面的截图中,您可以看到 mysql 服务在运行级别 2,3,4,5 上已启用。您可以使用以下命令禁用 MySQL:
|
1 |
sudo update-rc.d mysql disable |
输出如下所示。请注意,该服务已在所有运行级别停止:

运行以下命令以查看目录内容:
|
1 |
ls -l /etc/rc2.d |
请看下面输出中的 mysql 行:
|
1 |
lrwxrwxrwx 1 root root 15 Dec 11 05:28 K01mysql -> ../init.d/mysql |
输出显示符号链接已更改为 K,表示 Kill(停止)。因此,默认情况下,MySQL 在运行级别 2 下不会自动启动。每当您在 System V 中启用或禁用服务时,就会发生这种情况。只要服务的默认运行级别目录下存在 S(启动)脚本,init 守护进程就会在系统启动期间启动该服务。
要再次启用该服务,请输入以下命令:
|
1 |
sudo update-rc.d mysql enable |
步骤 6:测试系统崩溃后的 MySQL 启动行为
在本节中,我们将讨论 System V 如何处理服务崩溃。您可以使用这些知识来配置自定义服务在崩溃后的行为。
在本教程的第一部分中,我们对 /etc/inittab 文件进行了更改,以使 MySQL 在崩溃后能够自动启动。我们添加了以下行来启用此行为:
|
1 |
ms:2345:respawn:/bin/sh /usr/bin/mysqld_safe |
我们可以通过做一些测试来检查这种行为。首先,输入以下命令重启您的 VPS:
|
1 |
sudo reboot |
重启后,检查 mysql_safe 和 mysqld 正在运行的进程 ID,输入以下命令获取进程 ID:
|
1 |
ps -ef | grep mysql |
我们得到的输出是:
|
1 2 |
hackins 1836 1 0 07:30 ? 00:00:00 /bin/sh /usr/bin/mysqld_safe mysql 186338 907 0 07:30 ? 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 |
记下进程 ID。在我们的例子中,它们是 1836 和 186338。现在,通过输入以下命令并使用 -9 参数杀死进程来模拟崩溃。请记得替换为您自己的进程 ID:
|
1 2 |
sudo kill -9 1836 sudo kill -9 186338 |
几分钟后,运行以下命令检查 MySQL 的状态:
|
1 |
sudo service mysql status |
输出结果表明 MySQL 正在运行,这意味着它在模拟崩溃后已重新启动。如果您再次运行 ps -ef | grep mysql 命令,您会发现 mysqld_safe 和 mysqld 进程都在运行,尽管它们拥有新的 ID。
您可以尝试多次杀死这些进程,它们会在几分钟后重新启动。这种行为是通过我们在 /etc/inittab 文件中添加的那一行实现的。这就是在 System V 中配置服务在崩溃后自动重启的方法。要再次查看语法,请参阅本教程的第一部分。
某些自定义服务可能存在漏洞,在多次重试后无法重新启动。init 守护进程会尝试重新启动服务,但如果它在两分钟内失败超过十次,Linux 系统将禁用该服务长达 5 分钟。这有助于保持系统稳定,并确保系统资源不会浪费在不断崩溃的服务上。检查系统日志以找出自定义应用程序中需要修复的问题是一个好主意。
Upstart 简介
长期以来,System V init 系统对 Linux 发行版至关重要。然而,技术总是在不断进步。得益于开源社区的支持,Linux 生态系统得到了极大的发展。System V 以串行方式加载任务和服务,这带来了复杂性并消耗了时间。此外,现代可插拔存储介质的引入(System V 在设计时并未考虑这一点)推动了对不同 init 系统的需求。
Ubuntu 的开发人员开始着手开发另一个初始化系统。该 init 系统旨在实现更快的操作系统加载速度、确保优雅地清理崩溃的服务、保持系统服务之间的依赖关系可预测,并兼顾可插拔存储介质。Upstart 守护进程由此诞生。
与 System V init 相比,Upstart init 具有以下几个优势:
- Upstart 不会像 System V 那样串行加载服务,从而缩短了系统的启动时间。
- 它的设计旨在通过优雅的清理和服务重启来更好地处理崩溃的服务。
- Upstart 使用灵活的事件系统来定制各种状态下服务的处理方式。
- 该 init 避免了像 System V 那样用于加载和管理服务的复杂 shell 脚本。Upstart 使用易于理解和修改的简单配置文件。
- Upstart 在构建时考虑了向后兼容性。/etc/init.d/rc 脚本仍然运行以管理原生的 System V 服务。
- 它避免了保留冗余的符号链接(这些链接都指向同一个脚本)。
Upstart 事件
Upstart 是基于事件的,允许将多个事件与同一个服务相关联。这种基于事件的架构确保了灵活的服务管理。每个事件都可以调用特定于该事件的 shell 脚本。
Upstart 事件包括:
- Starting
- Started
- Stopping
- Stopped
在事件之间,服务可以处于各种状态,包括但不限于:
- Waiting
- Pre-start
- Starting
- Post-start
- Running
- Pre-stop
- Stopping
- Post-stop
可以配置 Upstart init 针对这些状态中的每一个采取行动,因此其设计非常灵活。
Upstart Init 启动顺序
Upstart init 在启动时运行 /etc/init.d/rc 脚本,就像 System V 一样。该脚本正常运行任何 System V init 脚本,以确保向后兼容性。
Upstart 配置文件位于 /etc/init 目录中,因此它默认会查找该目录,并执行该目录下配置文件中的 shell 命令。
Upstart 配置文件
Upstart init 使用服务配置文件来控制服务,这与 System V 中使用的 bash 脚本不同。这些服务配置文件的命名标准是 service_name.conf.
这些文件包含纯文本内容,分为不同的部分,称为 stanza(节)。每个 stanza 描述服务的不同状态及其行为。不同的 stanza 控制服务的不同事件。例如,waiting、pre-start、start、pre-stop、stopping 等。
一个 stanza 包含 shell 命令,从而可以为每个服务的每个事件启动多个操作。此外,每个服务配置文件都指定了以下两点:
- 服务应该在哪些运行级别(runlevel)启动和停止。
- 如果服务崩溃,是否应该重新生成(respawn)。
Upstart 目录结构
Upstart 服务配置文件位于 /etc/init 目录。请勿将其与 /etc/init.d.
Upstart 示例
在本示例中,我们将了解 Upstart 如何在系统启动期间以及发生崩溃时处理服务。我们将更详细地解释本教程第一部分中展示的实际示例。
步骤 1:登录 Ubuntu 14.0.4 服务器
首先,为了测试 Upstart,我们将使用第二个 VPS,即运行 Ubuntu 14.0.4 的那个。这是因为该 Linux 发行版原生实现了 Upstart。如果您使用的是 Windows,请使用 ssh 或 putty。您必须使用具有 root 或 sudo 权限的用户登录。我有一个名为 hackins 的用户,我是这样登录的:
|
1 |
ssh hackins@my_server_ip |
替换为您的 root/sudo 用户和服务器的公网 IP 地址。然后,按回车键,并提供密码或密码短语。
步骤 2:检查 init 和 rc 目录
Upstart 配置文件存储在 /etc/init 目录中。这是您在创建新服务配置文件时将使用的目录。
要列出 /etc/init 目录中的配置文件名称,请执行以下命令:
|
1 |
sudo ls -l /etc/init/ | less |
正如您从上述命令的输出中看到的那样,许多服务都在 Upstart 下运行。按 Q 键退出 less。
如果您运行以下命令来列出 rc 目录中 System V 的服务配置文件,您将只能看到几个:
|
1 |
sudo ls -l /etc/rc3.d/* | less |
步骤 3:检查 Upstart 文件
在本教程的第一部分中,我们使用了一个 mysql.conf 文件来了解服务器配置。为了增加我们的知识,让我们使用一个不同的配置。 cron 配置文件是一个不错的选择。输入以下命令打开该文件:
|
1 |
sudo nano /etc/init/cron.conf |
您应该会得到类似于下图所示的输出:

该脚本非常简单。请注意以下重要字段: start on, stop on, fork,以及 respawn。让我们定义一下这些指令的作用:
- start on 指示 Ubuntu 在系统进入运行级别 2、3、4 或 5 时启动 cron 守护进程。它不会在未在此处指定的其他运行级别(即 0、1 或 6)上运行。
- stop on 指示 Ubuntu 停止正在运行的守护进程。但是,在这种情况下,有一个感叹号(!),这是一个否定符号。该脚本不应在感叹号之后的运行级别停止:2,3,4,5。
- fork 指令指示 Upstart 将进程与控制台分离,并使其在后台运行。
- respawn 指令指示系统在 cron 因任何原因崩溃时自动启动它。
按 Ctrl X 退出编辑器,无需输入任何内容。
其他 upstart 配置文件也遵循相同的结构,包含用于 start、stop 和 respawn 的 stanza。某些配置文件可能还包含用于 pre-start、pre-stop、post-start 等的其他脚本块。这些代码块告诉系统在进程处于定义的任何状态时要执行什么操作。
步骤 4:测试系统启动后 MySQL 服务的启动行为
MySQL 默认设置为在系统启动后自动启动。我们将尝试禁用它并观察其行为。在 Upstart 中,可以通过在 service_name.override 目录下创建一个名为 /etc/init/ 的文件来禁用服务。该文件的内容只有一个单词:manual.
让我们看看如何使用此命令来禁用 MySQL 服务。输入以下命令以使用 nano 编辑器打开该文件:
|
1 |
sudo nano /etc/init/mysql.override |
在打开的文件中添加以下行:
|
1 |
Manual |
按 Ctrl + O 保存更改,然后按 Enter。按 Ctrl + X 退出编辑器。运行以下命令重启服务器:
|
1 |
sudo reboot |
等待几分钟,然后重新登录。重新登录后,输入以下命令测试 MySQL 服务的状态:
|
1 |
sudo initctl status mysql |
输出将表明该服务未在运行:
|
1 |
mysql stop/waiting |
这表明 MySQL 在系统启动后没有自动启动。接下来,打开 MySQL 配置文件并检查 start 指令是否已更改:
|
1 |
sudo cat /etc/init/mysql.conf | grep start\ on |
输出将表明没有任何更改:
|
1 |
start on runlevel [2345] |
这意味着,如果您的服务没有自动启动,而您只检查该服务的配置文件(service_name.conf),您可能找不到错误。您还应该检查该目录下是否存在 service_name.override 文件。
输入以下命令删除覆盖文件并重新启用 MySQL 服务。然后,重启服务器:
|
1 2 |
sudo rm -f /etc/init/mysql.override sudo reboot |
服务器启动后,再次测试 MySQL 服务的状态:
|
1 |
sudo initctl status mysql |
它应该显示 MySQL 服务已自动启动。
步骤 5:测试系统崩溃后 MySQL 服务的启动行为
默认情况下,如果 MySQL 服务崩溃,它会自动重启。要停止此行为,我们将编辑 mysql.conf 文件。输入以下命令以使用 nano 编辑器打开该文件:
|
1 |
sudo nano /etc/init/mysql.conf |
找到 respawn 指令行,并使用以下内容将其注释掉:#:
|
1 2 |
# respawn # respawn limit 2 5 |
执行以下命令重启服务:
|
1 2 |
sudo initctl stop mysql sudo initctl start mysql |
我们使用上述命令来停止和启动服务,因为在这里使用 initctl restart 或 initctl reload 将不起作用。当您运行启动 MySQL 服务的命令时,输出将显示 MySQL 的 PID,如下所示:
|
1 |
mysql start/running, process 1439 |
我们的 PID 是 1439。接下来,记下您运行命令时获得的值,我们将在下一步中使用它。要模拟崩溃,请使用以下命令终止 MySQL 进程,请记住替换为您上面解释的 PID:
|
1 |
sudo kill -9 1439 |
要检查 MySQL 在崩溃后是否重启,请等待几分钟,然后输入以下命令:
|
1 |
sudo initctl status mysql |
输出将表明 MySQL 已停止:
|
1 |
mysql stop/waiting |
尝试多检查几次状态,看看是否有任何变化。您会注意到 MySQL 保持停止状态。这是因为服务配置文件中没有 respawn 指令(即我们注释掉的那些指令)。请查看本教程的第一部分,以获取有关 respawn 指令的更多说明。
为什么我们要向您展示如何在系统启动或崩溃后禁用服务的自动重启?这主要是为了故障排查。例如,如果您的服务启动后不断崩溃,您可能希望禁用它以便进行排查,并保持系统稳定。如果您恰好升级到原生自带 systemd 的新 Linux 发行版,您还可以阻止某些旧的服务配置自动重启,这将在下一节中讨论。
Systemd 简介
Systemd 是最新的初始化系统,存在于最现代的 Linux 发行版中。它包含了构成现代 Linux 系统的许多组件。
Systemd 不仅管理服务,还管理整个 Linux 系统。在本节中,我们将重点介绍 systemd 如何在系统启动或崩溃后控制服务的行为。
Systemd 向后兼容 System V 初始化脚本和命令。因此,如果您有任何配置了 System V 的服务,它们也可以在 Systemd 下运行。大多数 Upstart 和 System V 管理命令已被修改以适用于 Systemd。Systemd 在启动时会将自己重命名为 init。存在一个 /sbin/init 文件,它符号链接到 /bin/systemd.
Systemd 配置文件:单元文件
Systemd 配置由单元文件组成。每个单元文件代表一个系统资源。虽然另外两个初始化系统(即 System V 和 Upstart)负责管理 Linux 系统的服务,但 Systemd 不仅管理服务守护进程,还管理其他类型的系统资源,例如设备操作系统路径、套接字、挂载点等。单元文件存储有关该资源的信息。
每个单元文件代表一个特定的系统资源,命名格式为 service_name.unit_type。这意味着您会找到像 home.mount、dbus.service、sshd.socket 等文件。单元文件是简单的文本文件,具有易于理解和修改的声明式语法。
目录结构
原生单元文件的主要位置是 /lib/systemd/system/ 目录。您创建的单元文件或系统管理员自定义创建的单元文件,以及其他修改过的原生单元文件,都存储在 /etc/systemd/system 目录中。
如果同名的单元文件同时存在于 /lib/systemd/system/ 和 /etc/systemd/system 目录中,systemd 将使用 /etc 目录下的那个文件。
当您启用某个服务以在开机启动或任何其他目标/运行级别启动时,系统会在 /etc/systemd/system 下的相应目录中创建该服务单元文件的符号链接。在 /etc/systemd/system 目录中的单元文件只是指向 /lib/systemd/system 目录中同名文件的符号链接。
Systemd 初始化顺序:目标单元
目标单元是独特类型的单元文件,通常以后缀 .target 结尾。目标单元与其他类型的单元文件不同,因为它们不代表某个特定的资源。相反,它们代表系统在给定时间点的整体状态。
为了实现这一点,目标单元会将属于特定状态的多个单元文件进行分组并启动。虽然 Systemd 目标和 System V 运行级别可以进行粗略的比较,但它们并不相同。目标单元文件使用的是名称而不是数字。例如,您会看到像 multi-user.target 这样的名称,而不是运行级别 3,或者 reboot.target 而不是运行级别 6。Linux 系统可能会以 multi-user.target 启动。在这种情况下,它基本上是将服务器带入运行级别 2、3 或 4,从而在启用网络的多用户文本模式下启动系统。
区别在于它如何将服务器带入该级别。System V 按顺序启动服务。另一方面,在系统启动时,systemd 会检查是否存在其他服务或资源,并确定它们的加载顺序。
systemd target 单元与 System V 运行级别之间的另一个区别是,使用 System V 的 Linux 发行版将仅存在于一个运行级别中。如果您修改运行级别,它将简单地切换到并存在于该新运行级别中。另一方面,target 单元文件可以是包含性的。此外,激活一个 target 单元可确保其他 target 单元作为其一部分被加载。例如,如果您启动具有图形用户界面的 Linux 系统,它将具有 graphical.target 被激活。这反过来又自动确保 multi-user.target 也被加载并激活。
以下是运行级别和 target 的对比表。
| 运行级别 (System V) | Target 单元 (systemd) |
| 运行级别 0 | poweroff.target |
| 运行级别 1 | rescue.target |
| 运行级别 2,3,4 | multi-user.target |
| 运行级别 5 | graphical.target |
| 运行级别 6 | reboot.target |
Systemd default.target
在 systemd 中,default.target 相当于 System V 中的默认运行级别。我们看到 System V 中的默认运行级别是在 inittab 文件中定义的。而在 systemd 中,我们有 default.target 文件。默认 target 文件存储在 /etc/systemd/system 目录中。它符号链接到 /lib/systemd/system 下的其中一个 target 单元文件。更改默认 target 仅仅意味着重新创建符号链接并修改系统的运行级别。
在 System V 中,inittab 指定了 Linux 将在哪个目录下找到 init 脚本。如前所述,这可以是任何 rc 目录。另一方面,systemd 默认 target 决定了在引导时加载的资源单元。所有定义的单元都会被加载。然而,并非所有单元都是并行加载的,也并非所有单元都是顺序加载的。资源单元的加载取决于它 wants 或 requires.
Systemd 依赖关系:Wants 和 Requires
在本节中,我们将讨论 Systemd 如何处理依赖关系。我们看到,使用 Upstart 时,在使用配置文件时可以并行加载服务。我们还讨论了 System V 如何使用运行级别来确定自动启动哪个服务,或者等待另一个服务或资源启动。类似地,Systemd 服务可以配置为在一个或多个 target 中加载,或者等待另一个服务或资源启动。
在 Systemd 中,一个 requires 另一个单元的单元文件在所要求的单元加载并激活之前不会启动。如果所要求的单元在第一个单元处于活动状态时加载失败,则第一个单元将停止。
这种行为确保了系统的稳定性。因此,一个 requires 特定资源(例如端口)可用且处于活动状态的服务可以被设置为等待,直到该资源可用(即端口已打开)。
相比之下,一个 wants 另一个单元的单元不会施加此类限制。如果被想要的单元在调用单元仍处于活动状态时停止,它不会停止。例如,graphical-target 模式下的一些非核心服务。
Systemd 示例
让我们看看如何配置 systemd 下服务的行为。
步骤 1:登录您的 VPS 实例
我们将使用 MySQL 作为实际服务,并使用 CentOS 7 作为服务器。为了实际操作这些步骤并理解这些概念,请登录您的 CentOS 7 VPS 或 在 CloudSigma 上创建一个。运行 CentOS 7、Debian 7 或 8,或者 Ubuntu 15 或更新版本的 VPS 适合本节,因为它们都自带 systemd。使用 ssh 命令登录,如果您使用的是 Windows,请使用 PuTTY:
|
1 |
ssh hackins@your_server_ip |
步骤 2:检查 default.target 文件和依赖关系
Systemd 的启动顺序遵循一条长长的依赖链,我们将在本节中详细讨论。
- default.target
default.target 文件控制在正常启动期间启动的服务。您可以使用以下命令列出默认 target 单元文件:
|
1 |
sudo ls -l /etc/systemd/system/default.target |
输出显示类似于下图所示的内容:
![]()
截图显示默认 target 符号链接到以下目录中的 multi-user.target 文件:/lib/systemd/system/ 目录。这意味着,默认情况下,系统将在 multi-user.target 下启动,相当于 运行级别 3.
- multi-user.target.wants
要查看 multi-user.target 文件需要的所有服务,请输入以下命令:
|
1 |
sudo ls -l /etc/systemd/system/multi-user.target.wants/*.service |
输出包含很多行,以下是一个片段:
|
1 2 3 4 5 6 7 8 |
lrwxrwxrwx. 1 root root 38 Dec 25 10:32 /etc/systemd/system/multi-user.target.wants/mysqld.service -> /usr/lib/systemd/system/mysqld.service lrwxrwxrwx. 1 root root 36 Dec 16 19:10 /etc/systemd/system/multi-user.target.wants/ntpd.service -> /usr/lib/systemd/system/ntpd.service lrwxrwxrwx. 1 root root 39 Dec 16 19:08 /etc/systemd/system/multi-user.target.wants/postfix.service -> /usr/lib/systemd/system/postfix.service lrwxrwxrwx. 1 root root 46 Dec 16 19:08 /etc/systemd/system/multi-user.target.wants/rhel-configure.service -> /usr/lib/systemd/system/rhel-configure.service lrwxrwxrwx. 1 root root 39 Dec 16 19:08 /etc/systemd/system/multi-user.target.wants/rsyslog.service -> /usr/lib/systemd/system/rsyslog.service lrwxrwxrwx. 1 root root 36 Dec 16 19:08 /etc/systemd/system/multi-user.target.wants/sshd.service -> /usr/lib/systemd/system/sshd.service lrwxrwxrwx. 1 root root 37 Dec 16 19:08 /etc/systemd/system/multi-user.target.wants/tuned.service -> /usr/lib/systemd/system/tuned.service lrwxrwxrwx. 1 root root 40 Dec 16 19:14 /etc/systemd/system/multi-user.target.wants/yum-cron.service -> /usr/lib/systemd/system/yum-cron.service |
如输出所示,它们是指向 /lib/systemd/system/ 目录中实际单元文件的符号链接。我们已经高亮显示了 mysqld.service,以向您指出它也是 multi-user.target 的一部分。如果您想确认某个服务是否已配置为开机启动,可以修改该文件的命令。例如,我们可以使用以下命令过滤输出以查找 mysql 或 cron 守护进程:
|
1 |
sudo systemctl show --property "Wants" multi-user.target | fmt -10 | grep mysql |
输出将显示:
|
1 |
mysqld.service |
要过滤 cron 守护进程的结果,请输入以下命令:
|
1 |
sudo systemctl show --property "Wants" multi-user.target | fmt -10 | grep cron |
输出将显示:
|
1 |
crond.service |
除了 multi-user.target 之外,还存在其他不同类型的 target,即 system-update.target 或 basic.target.
输入以下命令以查看 multi-user target 依赖于哪些 target:
|
1 |
sudo systemctl show --property "Requires" multi-user.target | fmt -10 |
输出显示:
|
1 |
Requires=basic.target |
这意味着,basic-target 必须首先加载,系统才能在 multi-user.target 模式下启动。
- basic-target
输入以下命令以查看 basic.target 还需要哪些其他 target:
|
1 |
sudo systemctl show --property "Requires" basic.target | fmt -10 |
输出将显示:
|
1 |
Requires=sysinit.target |
- sysinit.target
您可以运行以下命令来查看是否有任何所需的 target 用于 sysinit.target。该命令的语法是相同的。您可以继续修改它,以查看随着进行的深入,一个 target unit 需要哪些其他的 target unit。命令如下:
|
1 |
sudo systemctl show --property "Requires" sysinit.target | fmt -10 |
输出将显示没有所需的 unit 用于 sysinit.target。我们可以检查是否有其他服务和 target 被 by sysinit.target 所需要,使用以下命令:
|
1 |
sudo systemctl show --property "Wants" sysinit.target | fmt -10 |
输出显示了被 sysinit.target 所需要的服务和 target 的长列表。您可以在下面看到部分输出:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Wants=systemd-tmpfiles-setup-dev.service systemd-binfmt.service systemd-journald.service rhel-loadmodules.service dev-hugepages.mount systemd-modules-load.service rhel-autorelabel-mark.service plymouth-read-write.service sys-fs-fuse-connections.mount systemd-machine-id-commit.service systemd-random-seed.service systemd-udevd.service systemd-sysctl.service plymouth-start.service rhel-autorelabel.service proc-sys-fs-binfmt_misc.automount local-fs.target rhel-import-state.service sys-kernel-config.mount dev-mqueue.mount kmod-static-nodes.service systemd-update-utmp.service |
在使用 system4 进行系统初始化期间,系统不会仅停留在某一个 target 中。相反,它会在从一个 target 转移到另一个 target 的过程中,以依赖的方式加载服务。
步骤 3:检查 Unit 文件
让我们看看 unit 文件是什么样子的。我们在本教程的第一部分中使用了 MySQL 服务 unit 文件,我们将再次使用它。不过,我们也可以看看另一个服务 unit 文件——sshd unit 文件。输入以下命令打开 sshd 配置文件:
|
1 |
sudo nano /etc/systemd/system/multi-user.target.wants/sshd.service |
下面是显示文件中各行的截图:

如您所见,文件中列出的代码块使其易于理解并在必要时进行修改。以下是一些需要理解的重要指令:
- After – After 子句告诉系统仅在指定的 target 和服务加载后才加载该服务。在这种情况下,SSHD 服务将在 network target 和 keygen 服务加载后加载。
- Wants – Wants 子句显示了哪些 target 需要此服务。在这种情况下, ssh-keygen.service 需要 sshd.service。但是,如果 sshd 失败或崩溃,它不会关闭 ssh-keygen.service.
按 Ctrl + X 关闭编辑器。
步骤 4:在系统启动时测试 MySQL 服务的启动行为
在本节中,我们将向您展示如何在系统启动时更改和测试 MySQL 服务的行为。在上一节中,我们看到 mysqld.service 被 multi-user.target 所需要。因此,它将在开机时自动启动。
您可以通过运行以下命令来禁用该服务:
|
1 |
sudo systemctl disable mysqld.service |
运行该命令显示已从 /etc/systemd/system/multi-user.target.wants/ 目录中删除了 mysql 符号链接。为了对此进行测试,请运行以下命令来测试 MySQL 是否仍被 multi-user.target:
|
1 |
sudo systemctl show --属性 "Wants" multi-user.target | fmt -10 | grep mysql |
该命令不返回任何内容。如果您尝试重启服务器并检查 MySQL 状态,它将不会运行,这意味着它在开机时没有自动启动。
现在使用以下命令重新启用该服务:
|
1 |
sudo systemctl enable mysqld.service |
输出将显示一个符号链接。如果您重启服务器,MySQL 应该会自动启动。启用 Systemd 服务会在默认 target 的 wants 目录中创建一个符号链接。禁用 Systemd 服务会从 wants 目录中删除该符号链接。
步骤 5:测试服务崩溃后 MySQL 服务的启动行为
默认情况下,MySQL 服务在崩溃时会自动启动。我们可以在 MySQL 的 Systemd 配置文件中禁用此行为。首先,让我们看看该文件。输入以下命令打开文件:
|
1 |
sudo nano /etc/systemd/system/multi-user.target.wants/mysqld.service |
下图显示了输出:

Restart 指令的值设置为 on-failure。这意味着 MySQL 服务将在非正常退出代码、超时或非正常信号后重新启动。下表显示了来自 联机帮助页(man page).
| Restart 设置/退出原因 | no | always | on-success | on-failure | on-abnormal | on-abort | on-watchdog |
| 正常退出代码或信号 | X | X | |||||
| 非正常退出代码 | X | X | |||||
| 非正常信号 | X | X | X | X | |||
| 超时 | X | X | X | ||||
| 看门狗 (Watchdog) | X | X | X | X |
Systemd unit 文件中两个重要的指令是 Restart 和 RestartSec。它们控制服务的崩溃行为。Restart 指定服务何时应重新启动,而 RestartSec 指定崩溃后重新启动前应等待多长时间。要禁用重新启动行为,请通过在行首添加 # 来注释掉 Restart 指令,如下所示:
|
1 |
# Restart=always |
现在,重新加载系统守护进程,然后使用以下命令重新加载 mysqld 服务:
|
1 2 |
sudo systemctl daemon-reload sudo systemctl restart mysqld.service |
接下来,运行以下命令以查找 MySQL 服务的 Main PID:
|
1 |
sudo systemctl status mysqld.service |

我们测试的 Main PID 是 23809。请记下您的 PID 以在下一个命令中使用。使用 kill -9 命令,通过终止进程来模拟崩溃。另外,请记住替换为您在测试中获得的进程号:
|
1 |
sudo kill -9 23809 |
如果您运行检查 MySQL 状态的命令,您会发现它处于非活动状态,并且未能重新启动:
|
1 |
sudo systemctl status mysqld.service |

只要 mysqld.service 配置文件中的 Restart 指令被注释掉,它就会一直保持在失败状态。这模拟了服务停止且无法恢复的崩溃情况。
要重新启用该服务,您可以编辑 mysqld.service 配置文件,取消注释 Restart 指令,然后保存并关闭。像您之前所做的那样,重新加载守护进程并重新启动服务。这会将服务恢复到其初始配置,现在它可以在崩溃后自动启动。最后,这就是配置服务在崩溃后自动启动的全部内容。如果您想配置服务在崩溃后自动启动,您只需在服务 unit 文件的 Restart 指令(如果需要,您还可以添加 RestartSec 指令),位于 [Service] 部分下方。
结论
在本教程中,我们讨论了 Linux 在启动期间或崩溃后如何处理服务。为了理解 Linux 系统初始化过程,我们讨论了 Linux 使用的三种 init 系统:System V、Upstart 和 Systemd。我们讨论了它们的演变,以及每个 init 进程如何与系统重启或崩溃后自动启动服务相关联地工作。
由于 init 守护进程和 Linux 发行版都在不断演变,请记得检查您正在运行的 Linux 发行版版本,以便了解您的系统原生支持哪种 init 守护进程。
Linux 原生应用程序和大多数第三方应用程序在系统启动或崩溃后已经会自动启动,因此您无需执行任何操作。当您配置自定义服务的启动和重启行为,或者对不断崩溃的服务进行故障排除时,本教程中的知识至关重要。
祝您计算愉快!
评论
暂无评论。发表第一条评论吧。