从 init 到 systemd:Linux 守护进程管理的架构演进

0.简介

在linux的发展历程中,所有组件都在从简单但低效的初期方案向着复杂而高度优化的现代体系演进,守护进程的管理也不例外。本文将从SysV init和Systemd的架构、实现机制和使用方式进行介绍,以此来说明架构演进的驱动力(解决的问题)和对我们设计的启发。

1. SysV init

SysV init(System V init)是早期 Linux 的初始化系统,基于层级化和顺序化的设计理念,采用串行、脚本驱动的方式管理守护进程。接下来我们将从其核心进程与外部交互结构,工作流程,使用方式,问题总结四个方面来进行介绍。

1.1 整体结构

整体结构可以分为四个部分,中心为核心进程,也就是init,其核心职责包括配置文件读取,运行级别设置以及任务的启动;左侧为配置文件,主要在/etc下init的目录中;右侧是服务脚本,由要启动的服务提供;下面就是系统中一些其他进程,都由init直接或者间接启动。

从 init 到 systemd:Linux 守护进程管理的架构演进

1.2 工作流程

工作流程我们来看启动和关闭时的执行流程:

1)启动流程

init进程 → 读取 /etc/inittab → 确定运行级别X → 执行 /etc/rcX.d/S*.sh 脚本 → 启动服务进程  

从 init 到 systemd:Linux 守护进程管理的架构演进

2)关闭流程

init 0 → 执行 /etc/rc0.d/K*.sh 脚本 → 停止服务进程 → 关闭系统

1.3 使用方式

使用方式我们从两个方面来看,一个是配置文件,一个是运行中的修改。

1.3.1 配置文件

1)/etc/inittab:SysV init系统中的核心配置文件,其条目格式为:

id:runlevels:action:process

1. 字段说明

字段

含义

示例

id

条目的唯一标识符(1-4个字符)

1

, si, tty1

runlevels

适用的运行级别(数字组合,留空表明所有级别)

235

, 3, “

action

执行动作类型

respawn

, initdefault

process

要执行的命令或进程

/sbin/getty

2. 关键 action 类型

action

功能

典型应用场景

initdefault

设置默认运行级别

id:3:initdefault:

sysinit

系统初始化时执行

挂载文件系统

respawn

进程终止后自动重启

终端登录(getty)

wait

执行一次并等待完成

网络服务初始化

ctrlaltdel

响应 Ctrl+Alt+Del 组合键

安全关机

once

执行一次不等待

启动时任务

3. 典型配置示例

# 设置默认运行级别为3(多用户文本模式)
id:3:initdefault:
# 系统初始化脚本
si::sysinit:/etc/rc.d/rc.sysinit
# 为运行级别2/3/5启动服务
l0:0:wait:/etc/rc.d/rc 0
l3:3:wait:/etc/rc.d/rc 3
# 终端配置(自动重启)
1:2345:respawn:/sbin/getty 38400 tty1
2:2345:respawn:/sbin/getty 38400 tty2
# Ctrl+Alt+Del处理
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

4. 运行级别说明

级别

模式

说明

0

Halt

关机

1

Single user mode

维护模式

2

Multi-user (no NFS)

基本多用户

3

Full multi-user

标准服务器模式

4

Unused

自定义

5

X11

图形界面模式

6

Reboot

重启

2)/etc/rc.d/rc:主控制脚本,负责运行指定运行级别对应的 rcX.d/ 目录中的脚本

3)/etc/rc.d/rcX.d/:包含特定运行级别(X=0-6)的符号链接,指向 /etc/init.d/ 中的实际脚本。

4)/etc/init.d/:包含所有系统服务的启动、停止和管理脚本。每个服务对应一个独立的 Shell 脚本。

1.3.2 运行在的修改

运行中修改常见操作如下:

##切换运行级别
init [0-6]  # 直接切换到指定运行级别,例如:
init 3      # 切换到多用户文本模式
init 5      # 切换到图形界面模式
init 6      # 重启系统
#启动服务
/etc/init.d/network start     # 启动网络服务
/etc/init.d/ssh restart       # 重启SSH服务
/etc/init.d/httpd status      # 查看HTTP服务状态
##设置服务在指定级别启动
chkconfig --level 35 network on  # 在运行级别3和5启动网络服务
chkconfig --level 24 sshd off    # 在运行级别2和4关闭SSH服务

1.4 问题总结

从上面对于SysV init的分析,可以发现其提供了简单轻量的服务管理功能,但问题还是比较明显:

1)性能瓶颈:执行所有脚本,其采用顺序执行,并没有很好的利用多核优势。

2)配置复杂:配置文件多且复杂,缺少统一管理;脚本规范严苛,编写成本高。

3)服务管理能力薄弱:服务之前的执行顺序依赖于脚本名称顺序(如 /etc/rcX.d/S50nginx 中的 50),容易出现循环依赖或者顺序错误;而且无法自动检测服务运行状态变化。

2. Systemd

Systemd是SysV init的替代产品,其相较于init来说,并不是一个进程,而是提供了许多服务,我们将从其架构、设计思路以及使用方式三个方面对其进行探讨。

2.1 架构

Systemd是分层的,其最上层是各种工具,像系统管理的主要工具systemctl;其下层是各种后台进程,库以及内核支持,这就为Systemd提供了超级丰富的功能。

从 init 到 systemd:Linux 守护进程管理的架构演进

2.2 设计思路

设计思路部分我们主要看其如何去解决SysV init的问题。

2.2.1 并行启动的实现

systemd的并行启动是通过依赖图分析和异步执行两大核心技术实现的:

1)依赖图:通过在启动前根据配置文件中的依赖关系构造有向无环图,每个节点代表一个单元,边表明依赖关系。

2)异步执行:使用多线程异步架构,当某个单元依赖条件满足时,立即放入工作队列异步执行。

2.2.2 配置文件的规范管理

systemd的配置文件体系通过统一的抽象模型,声明式的语法和模块化设计简化了配置文件管理。

1)层次划分:不再分散在各种目录里,而是划分为三层:

系统默认:
/usr/lib/systemd/system/(由包管理器维护)
用户自定义:
/etc/systemd/system/(覆盖系统默认)
运行时配置:
/run/systemd/system/(动态生成)

2)语法统一:使用风格统一的Unit文件取代分散的各种shell脚本。

3)解决命名限制:不再依赖名称顺序启动,而是自己分析依赖图。

2.2.3 服务管理精细化

1)全生命周期管理:使用cgroup来作为控制组,子进程会继承父进程的cgroup,这样就可以启停某个服务相关全量的相关进程。

2)服务监控和资源控制:通过cgroup自动对服务进行监控,根据配置文件尝试重启和资源限制等。

2.3 使用方式

通过上面的介绍,我们知道有依赖图的概念,那么依赖图的来源就是统一的Unit概念,Unit概念分为以下几种:

Service unit:系统服务
Target unit:多个 Unit 构成的一个组
Device Unit:硬件设备
Mount Unit:文件系统的挂载点
Automount Unit:自动挂载点
Path Unit:文件或路径
Scope Unit:不是由 Systemd 启动的外部进程
Slice Unit:进程组
Snapshot Unit:Systemd 快照,可以切回某个快照
Socket Unit:进程间通信的 socket
Swap Unit:swap 文件
Timer Unit:定时器

如何定义一个Unit的配置文件我们通过一个sshd例子来看。使用下面命令即可查看。

systemctl cat sshd

从 init 到 systemd:Linux 守护进程管理的架构演进

其分为三个部分:

1)Unit区域:元数据部分,同时在其中配置和别的Unit 的顺序关系。

2)Service区域:只有service类型的unit才有,定义启动中的一些命令,状态等。

3)Install区域:定义如何启动,何时启动。

官方文档:

https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html

3.总结

上面我们了解了初期的设计SysV Init,其提供了简单轻量的服务来实现了守护进程的管理,但由于其带来的问题随着时代的进步影响越来越大,所以产生了systemd,通过各种设置来解决这些问题;但systemd没有问题吗?从其架构图中可以看到其超级重量级,且依赖于linux内核,违反了违反”keep simple, keep stupid”的Unix哲学。所以实则很难有完美的设计,都是对于当下问题的权衡,演进的意义也在于此,以当下的眼光去看过去的设计,通过改善找到当下最好的解决方法。

© 版权声明

相关文章

1 条评论

  • 头像
    Do医生 投稿者

    收藏了,感谢分享

    无记录
    回复