浅析 Linux 初始化 init 系统,第 3 部分: Systemd

近年来,Linux 系统的 init 进程经历了两次重大的演进,传统的 sysvinit 已经逐渐淡出历史舞台,新的 UpStart 和 systemd 各有特点,越来越多的 Linux 发行版采纳了 systemd。本文简要介绍了这三种 init 系统的使用和原理,每个 Linux 系统管理员和系统软件开发者都应该了解它们,以便更好地管理系统和开发应用。本文是系列的第 3 部分,主要讲述 systemd 的特点和使用。

Systemd 的简介和特点

Systemd 是 Linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit 固有的缺点,提高系统的启动速度。systemd 和 Ubuntu 的 upstart 是竞争对手,预计会取代 UpStart,实际上在作者写作本文时,已经有消息称 Ubuntu 也将采用 systemd 作为其标准的系统初始化系统。

Systemd 的很多概念来源于苹果 Mac OS 操作系统上的 launchd,不过 launchd 专用于苹果系统,因此长期未能获得应有的广泛关注。Systemd 借鉴了很多 launchd 的思想,它的重要特性如下:

同 SysVinit 和 LSB init scripts 兼容

Systemd 是一个”新来的”,Linux 上的很多应用程序并没有来得及为它做相应的改变。和 UpStart 一样,systemd 引入了新的配置方式,对应用程序的开发也有一些新的要求。如果 systemd 想替代目前正在运行的初始化系统,就必须和现有程序兼容。任何一个 Linux 发行版都很难为了采用 systemd 而在短时间内将所有的服务代码都修改一遍。

Systemd 提供了和 Sysvinit 以及 LSB initscripts 兼容的特性。系统中已经存在的服务和进程无需修改。这降低了系统向 systemd 迁移的成本,使得 systemd 替换现有初始化系统成为可能。

更快的启动速度

Systemd 提供了比 UpStart 更激进的并行启动能力,采用了 socket / D-Bus activation 等技术启动服务。一个显而易见的结果就是:更快的启动速度。

为了减少系统启动时间,systemd 的目标是:

尽可能启动更少的进程尽可能将更多进程并行启动

同样地,UpStart 也试图实现这两个目标。UpStart 采用事件驱动机制,服务可以暂不启动,当需要的时候才通过事件触发其启动,这符合第一个设计目标;此外,不相干的服务可以并行启动,这也实现了第二个目标。

下面的图形演示了 UpStart 相对于 SysVInit 在并发启动这个方面的改进:

图 1. UpStart 对 SysVinit 的改进

假设有 7 个不同的启动项目, 比如 JobA、Job B 等等。在 SysVInit 中,每一个启动项目都由一个独立的脚本负责,它们由 sysVinit 顺序地,串行地调用。因此总的启动时间为 T1+T2+T3+T4+T5+T6+T7。其中一些任务有依赖关系,比如 A,B,C,D。

而 Job E 和 F 却和 A,B,C,D 无关。这种情况下,UpStart 能够并发地运行任务{E,F,(A,B,C,D)},使得总的启动时间减少为 T1+T2+T3。

这无疑增加了系统启动的并行性,从而提高了系统启动速度。但是在 UpStart 中,有依赖关系的服务还是必须先后启动。比如任务 A,B,(C,D)因为存在依赖关系,所以在这个局部,还是串行执行。

让我们例举一些例子, Avahi 服务需要 D-Bus 提供的功能,因此 Avahi 的启动依赖于 D-Bus,UpStart 中,Avahi 必须等到 D-Bus 启动就绪之后才开始启动。类似的,livirtd 和 X11 都需要 HAL 服务先启动,而所有这些服务都需要 syslog 服务记录日志,因此它们都必须等待 syslog 服务先启动起来。然而 httpd 和他们都没有关系,因此 httpd 可以和 Avahi 等服务并发启动。

Systemd 能够更进一步提高并发性,即便对于那些 UpStart 认为存在相互依赖而必须串行的服务,比如 Avahi 和 D-Bus 也可以并发启动。从而实现如下图所示的并发启动过程:

图 2. systemd 的并发启动

所有的任务都同时并发执行,总的启动时间被进一步降低为 T1。

可见 systemd 比 UpStart 更进一步提高了并行启动能力,极大地加速了系统启动时间。

Linux 引导方式systemd upstart sysV

为什么systemd会被如此迅速的采用?

systemd 与 sysVinit 彩版对照表

Linux Systemd——在RHEL/CentOS 7中启动/停止/重启服务

太有用了!用systemd命令来管理Linux系统!

systemd 提供按需启动能力

当 sysvinit 系统初始化的时候,它会将所有可能用到的后台服务进程全部启动运行。并且系统必须等待所有的服务都启动就绪之后,才允许用户登录。这种做法有两个缺点:首先是启动时间过长;其次是系统资源浪费。

某些服务很可能在很长一段时间内,甚至整个服务器运行期间都没有被使用过。比如 CUPS,打印服务在多数服务器上很少被真正使用到。您可能没有想到,在很多服务器上 SSHD 也是很少被真正访问到的。花费在启动这些服务上的时间是不必要的;同样,花费在这些服务上的系统资源也是一种浪费。

Systemd 可以提供按需启动的能力,只有在某个服务被真正请求的时候才启动它。当该服务结束,systemd 可以关闭它,等待下次需要时再次启动它。

Systemd 采用 Linux 的 Cgroup 特性跟踪和管理进程的生命周期

init 系统的一个重要职责就是负责跟踪和管理服务进程的生命周期。它不仅可以启动一个服务,也必须也能够停止服务。这看上去没有什么特别的,然而在真正用代码实现的时候,您或许会发现停止服务比一开始想的要困难。

服务进程一般都会作为精灵进程(daemon)在后台运行,为此服务程序有时候会派生(fork)两次。在 UpStart 中,需要在配置文件中正确地配置 expect 小节。这样 UpStart 通过对 fork 系统调用进行计数,从而获知真正的精灵进程的 PID 号。比如图 3 所示的例子:

图 3. 找到正确 pid

如果 UpStart 找错了,将 p1`作为服务进程的 Pid,那么停止服务的时候,UpStart 会试图杀死 p1`进程,而真正的 p1“进程则继续执行。换句话说该服务就失去控制了。

还有更加特殊的情况。比如,一个 CGI 程序会派生两次,,从而脱离了和 Apache 的父子关系。当 Apache 进程被停止后,该 CGI 程序还在继续运行。而我们希望服务停止后,所有由它所启动的相关进程也被停止。

一直觉得人应该去旅行,在年轻的时候,

浅析 Linux 初始化 init 系统,第 3 部分: Systemd

相关文章:

你感兴趣的文章:

标签云: