目 录CONTENT

文章目录

系统服务管理器 —— systemd

TalentQ
2025-09-02 / 0 评论 / 0 点赞 / 4 阅读 / 0 字

1 systemd 概述

systemd(官网:systemd.io) 是现代 Linux 系统的初始化系统和服务管理器,被广泛用于众多主流发行版,如 Ubuntu、CentOS 等。它旨在代替传统的 SysV init 和 Upstart,提供更高效的系统启动速度、更强的服务管理能力以及更丰富的功能集。

1.1 历史

历史上使用 init 进程作为第一个进程来管理系统启动,但是 init 的缺点非常明显,它只能串行执行且脚本复杂,造成启动时间长、维护困难。

现代 Linux 系统使用 systemd 取代了 init,作为第一个进程(PID为1)。它能够并行启动,并且提供一系列的工具便于使用。

1.2 名称

systemd 名称中的 “d” 是 daemon (守护进程)的意思,表示它要守护整个系统。systemd 的进程号 PID 为1,其它进程都是它的子进程。

1.3 原理与架构

systemd 架构图

systemd采用“并行启动”策略,利用服务间依赖关系图,实现多任务并发启动,显著提升系统启动速度。所有服务、挂载点、套接字等均抽象为 Unit(单元),并通过统一的方式进行管理。

主要组件:

主要组件

描述

systemd

主守护进程,PID为1,负责系统初始化、服务管理

systemctl

命令行工具,管理 systemd 单元

journalctl

日志管理工具,查看 systemd 日志

systemd-analyze

启动性能分析工具

2 Unit - 配置文件

systemd 支持多种类型的单元(Unit),每种类型对应不同的系统管理资源或管理对象。systemd 有 12 种 unit 类型(参考:Understanding systemd):

类型

后缀

描述

service

.service

管理后台服务进程(如 nginx.service),最常用的 unit

socket

.socket

管理套接字激活,支持按需启动服务

target

.target

管理一组 unit 的启动,类似于运行级别,或达到某个状态点

device

.device

管理内核设备(如挂在USB设备)

mount

.mount

管理文件系统挂载点

automount

.automount

管理自动挂载点

swap

.swap

管理 swap 分区或文件

timer

.timer

管理定时任务,可替代cron

path

.path

监控文件或目录的变化,触发服务

slice

.slice

管理 cgroup 层级,实现资源分组和限制

scop

.scop

管理由 systemd 外部启动的进程的生命周期

snapshot

.snapshot

当前 systemd 状态的快照,通常在对 systemd 做修改后回滚使用

2.1 unit 文件模板

[Unit]
...
[Unit Type]
...
[Install]
...

unit 文件通常由三部分组成:[Unit] [Unit Type][Install],其中 [Unit Type] 根据具体的 unit 而命名,例如对于 *.service 文件,就由 [Unit][Service][Install]三部分组成。对于具体的一个 unit 文件,允许只有三部分中的其中某几个部分。

2.2 unit 文件路径

系统级 unit 文件优先级(由高到低):

(当我们使用 sudo systemctl ... 指令时)

路径

描述

/etc/systemd/system/

管理员自定义的 unit 文件,优先被加载

/run/systemd/system/

运行时临时配置,仅本次启动有效

/usr/lib/systemd/system/

软件包安装时提供,默认配置

/lib/systemd/system/

某些发行版使用,作用同上

用户级 unit 文件优先级(由高到低):

(当我们使用 systemctl --user ... 指令时)

路径

描述

~/.config/systemd/user/

当前用户自定义的 unit 文件

/etc/xdg/systemd/user/

系统范围XDG配置

/etc/systemd/user/

系统范围配置

/usr/lib/systemd/system/

软件包安装时提供,默认配置

当有自定义 unit 文件的需求时,如果所有用户都用到,就放在 /etc/systemd/system/,如果只有当前用户用到,就放在 ~/.config/systemd/user/

2.3 常用参数

2.3.1 [Unit] 部分

参数

描述

Description

服务的描述信息

Documentation

帮助文档

After

仅定义启动顺序,不定义依赖关系。即使要求先启动的服务启动失败,本服务也依然会启动。

Before

仅定义启动顺序,不定义依赖关系。通常定义在关机前要关闭的服务,如 Before=shutdown.target

Wants

仅定义依赖关系,不定义启动顺序。弱依赖关系,表示本 unit 被启动时,systemd 会尝试启动 Wants 指定的 unit,但即使启动失败,也不影响本 unit 的启动。

Requires

仅定义依赖关系,不定义启动顺序。强依赖关系,表示本 unit 被启动时,systemd 会尝试启动 Requires 指定的 unit,,如果启动失败,本 unit 也会被停止或不启动。

Requisite

仅定义依赖关系,不定义启动顺序。强依赖关系,但是不会自动启动依赖的 unit,但如果依赖的 unit 没有启动,本 unit 也不会启动。适用于必须依赖某些unit,但希望依赖的 unit 由其他方式或手动启动。

BindsTo

强绑定依赖,如果被依赖的 unit 停止或失败,本 unit 也会被自动停止。

行为:启动本 unit 时,会自动启动被 BindsTo 指定的 unit (类似于 Requires)。但与 Requires 不同的是,如果被依赖的 unit 停止、崩溃或消失,本 unit 也会i被 systemd 自动停止。

场景:适用于“生命共同体”服务,例如挂载点和依赖其的服务,主服务和守护进程。

PartOf

属于某个 unit 的一部分。

行为:启动本 unit 不会自动启动被 PartOf 指定的 unit,但是停止或重启指定的 unit 时,本 unit 也会跟着被停止或重启。

场景:适合一组服务需要被统一管理的时候,比如多个协作服务要随着主服务一起停止/启动。

Conflicts

互斥依赖。被指定的 unit 不能和本 unit 同时处于 active 状态。

行为:启动本 unit 时,如果被指定的 unit 已经在运行,systemd 会先停止它;启动指定的 unit 时,如果本 unit 在允许,则本 unit 会被停掉。

场景:适合同类型但不能共存的服务,例如同一端口的两种web服务。

OnFailure

当本 unit 处于 failed 状态时,将启动指定的 unit。如果本 unit 设置了 Restart 参数,则在耗尽重启次数后才进入 failed 状态。

场景:用于通知、报警、自动修复、收集故障信息等。

ConditionPathExists

AssertPathExists

要求指定的路径存在,否则不启动也不报错,标记为 inactive(dead) 或 skipped。

要求指定的路径存在,否则启动失败,在日志中报错,标记为 failed。

ConditionPathIsDirectory

AssertPathIsDirectory

同上

ConditionPathIsReadWrite

AssertPathIsReadWrite

同上

ConditionDirectoryNotEmpty

AssertDirectoryNotEmpty

同上

ConditionFileNotEmpty

AssertFileNotEmpty

同上

ConditionFileIsExecutable

AssertFileIsExecutable

同上

2.3.2 [Unit Type] 部分

这里我们只对 unit 为 service 类型进行讨论。即 [Service] 部分。

参数

描述

Type

进程的启动类型,必须是下列值之一:simple, forking, oneshot, dbus, notify, idle,默认是 simple。

启动服务时,systemd 首先会fork一个systemd的子进程,该子进程使用exec()调用 ExecStart 指定的服务,该服务会替换当前进程,所以该服务进程就是systemd的子进程。

  • simple
    一旦 ExecStart 被执行,就立即返回,不用等待 ExecStart 的执行过程。
    适用于前台常驻进程

  • forking
    传统的Unix守护进程会先fork出一个子进程,然后父进程退出,子进程在后台运行。当 ExecStart 指定的是一个守护进程时,用 Type=forking。
    systemd会启动 ExecStart 指定的命令,监控其fork行为。当守护进程的父进程退出后,systemd将其子进程(后台守护进程)作为主进程进行管理。服务的 active 状态以父进程的退出为标志。
    可是systemd是如何追踪到由父进程fork出的子进程的呢?参考:simple和forking

  • oneshot
    通常用于只执行一次的命令(如初始化脚本),没有常驻进程。
    systemd 等待 ExecStart 指定的命令执行完毕,即会阻塞其他依赖该服务的服务的启动。
    如果设置了 RemainAfterExit=yes,则systemd会在该服务执行完毕后,仍标记其为 active 状态,方便其他服务依赖它。

User

指定服务进程以哪个用户身份运行

Nice

设置服务进程的优先级,nice值越高,优先级越低

WorkingDirectory

指定服务启动时的工作目录

ExecStart

指定服务主进程启动时要执行的命令

ExecStartPre

在 ExecStart 之前执行的命令。可以有多个、按顺序执行。通常用于做准备工作(如创建目录、检测环境等)

ExecStartPost

在 ExecStart 成功后执行的命令。可以有多个、按顺序执行。通常用于启动后收尾工作。

ExecStop

停止服务时执行的命令。用于优雅关闭服务或清理资源。

ExecStopPost

在 ExecStop 执行后(服务停止后)执行的命令,通常用于清理i日志等。

ExecReload

到收到 reload 信号(如 systemctl reload)时执行的命令。用于让服务重新加载配置而不中断服务。

Restart

指定服务异常退出时是否自动重启,以及重启策略。

  • no:不重启(默认值);

  • on-success:正常退出时(退出状态码为0)重启;

  • on-failure:失败时(退出状态码非0,包括被信号终止和超时)重启;

  • on-abnormal:只有被信号终止和超时,才会重启;

  • on-abort:当服务因接收到 未被捕捉的信号(如SIGABRT、SIGBUS、SIGSEGV)而终止时,才会重启;

  • on-watchdog:超时退出,才会重启;

  • always:不管是什么原因退出,总是重启;

RestartSec

指定自动重启前等待的秒数(间隔时间)

RemainAfterExit

可以设置为 yes 或 no,默认是 no。表示当该服务的所有进程退出后,是否将此服务视为 活动(active)状态。

适用于一次性任务,如初始化、配置脚本,希望 systemd 认为服务已完成并且处于 active 状态。

StartLimitInterval

StartLimitBurst

限制服务的启动频率。默认情况下,systemd 允许服务在 10 秒内最多启动 5 次(即 StartLimitInterval=10sStartLimitBurst=5)。

  • StartLimitInterval 的默认值取自 systemd 配置文件中的 DefaultStartLimitInterval=。如果将其设置为 0,则表示不对启动频率进行限制。

  • StartLimitBurst 的默认值则取自 DefaultStartLimitBurst=

这两个选项虽然经常与 Restart= 一起使用,但它们限制的是所有类型的启动,包括手动启动,而不仅仅是由于 Restart= 逻辑导致的自动重启。

需要注意的是,当因 Restart= 逻辑导致的重启次数超过启动频率限制后,systemd 会禁用自动重启(即不会在下一个时间窗口内继续尝试重启)。不过,如果此时手动重启服务,Restart= 逻辑会重新被激活。

另外,可以使用 systemctl reset-failed <服务名> 命令来清除服务的重启计数器,这通常用于在手动启动服务前,重置启动频率限制。

KillMode

用于定义 systemd 停止服务(如 sshd)时,如何处理与该服务相关的进程。其常用取值及含义如下:

  • control-group(默认值):会杀死当前控制组(cgroup)内的所有进程,包括主进程和所有子进程。

  • process:只会杀死主进程,不影响子进程。

  • mixed:主进程收到 SIGTERM 信号,子进程则直接收到 SIGKILL 信号。

  • none:不会自动杀死任何进程,只执行服务定义的 stop 命令,进程的终止需由 stop 命令自行处理。

2.3.3 [Install] 部分

参数

描述

WantedBy

声明该 unit 被哪些 target “想要”依赖,弱依赖。最常见的有:WantedBy=multi-user.target。

当用户运行 systemctl enable <服务名> 时,systemd 会自动在对应的 target 的 wants 目录下创建指向该 unit 的符号链接。这样在 target 激活时,会尝试激活该服务,但即使服务失败也不影响 target 的启动。

RequiredBy

声明该 unit 被哪些 target “必须”依赖,强依赖。

当用户运行 systemctl enable <服务名> 时,systemd 会自动在对应的 target 的 requires 目录下创建指向该 unit 的符号链接。这样在 target 激活时,若该服务启动失败,会导致整个 target 失败。

Alias

为 unit 文件创建一个或多个别名,便于通过不同的名称管理和操作该 unit。启动该 unit 时,systemd 会在该 unit 所在目录创建别名的符号链接。

例如,对于 clash.service,可以添加 Alias=proxy.service。

我们最常使用的是 servicetarget 两种 unit。其他的遇到了再具体学习。

3 Service - 服务

首先来看一个示例:

# Copyright @TalentQ All Reserved.


[Unit]
Description=clash for linux

[Service]
Type=simple
User=talentq
ExecStart=/home/talentq/.local/bin/clash

[Install]
WantedBy=multi-user.target

我们在 /etc/systemd/system/ 下创建文件 clash.service,并填入上面的内容:

cd  /etc/systemd/system/

sudo touch clash.service

重新加载 systemd 配置,让 systemd 发现刚刚创建的配置文件:

sudo systemctl daemon-reload

启动该服务:

sudo systemctl start clash
# sudo systemctl start clash.service # 加不加文件后缀都可以

设置开机自启动(如果有需要的话):

sudo systemctl enable clash

取消开机自启动:

sudo systemctl disable clash

4 Target - 运行级别

target 本身并不执行实际的任务或启动进程,而是用来组织和管理其他 unit 的启动顺序和分组。可以理解为系统运行到某个状态需要激活的所有服务和资源。不同的 target 就代表不同的运行级别。

常见的 target 有:

  • basic.target:基本系统服务,是其他 target 的基础

  • network.target:网络已启动,网络服务可在此之后启动

  • multi-user.target:多用户命令行环境,开机不进入图形化桌面,就是这个状态

  • graphical.target:图形界面环境,开机进入图像化桌面,就是这个状态

target 文件只包含 [unit] 部分,定义描述信息和依赖关系。Linux 系统开机的时候,根据 /usr/lib/systemd/system/default.target 这个符号链接指向的实际 target 文件,来决定启动到哪个运行级别。

# 查看默认的 target
systemctl get-default

# 修改默认的 target
sudo systemctl set-default xxx.target

# 激活某个 target
sudo systemctl start xxx.target

# 切换到某个 target
sudo systemctl isolate multi-user.target

5 常用工具

5.1 systemctl

管理和控制 unit。

# 启动服务
sudo systemctl start nginx.service

# 停止服务
sudo systemctl stop nginx.service

# 重启服务
sudo systemctl restart nginx.service

# 查看服务状态
systemctl status nginx.service

# 开机自动启动服务
sudo systemctl enable nginx.service

# 关闭开机自启动
sudo systemctl disable nginx.service

# 查看所有已激活 unit
sudo systemctl list-units

# 查看所有服务 unit
sudo systemctl list-units --type=service

# 查看默认 target
sudo systemctl get-default

# 设置默认 target
sudo systemctl set-default graphical.target

# 重新加载 systemd 配置
sudo systemctl daemon-reload

5.2 systemd-analyze

分析和优化系统启动性能。其中 systemd-analyze plot > boot.svg 将启动过程可视化,可以很好地分析拖慢启动的服务。

# 查看系统启动各阶段耗时
systemd-analyze

# 查看各服务启动耗时明细
systemd-analyze blame

# 显示服务启动的依赖关系图(SVG格式)
systemd-analyze plot > boot.svg

# 查看某个服务的依赖关系
systemd-analyze critical-chain nginx.service

5.3 journalctl

查询和过滤 systemd 日志。

# 查看所有日志
journalctl

# 按时间倒序查看最新日志
journalctl -r

# 实时滚动查看日志
journalctl -r

# 查看某个服务的日志
journalctl -u nginx.service

# 查看某个时间段的日志
hournalctl --since "2024-06-01 00:00:00" --until "2024-06-01 12:00:00"

# 查看指定优先级(如错误)的日志
journalctl -p err

# 查看本次启动后的日志
journalctl -b

# 查看上一次启动的日志
journalctl -b -1

# 查看自本次启动以来,systemd 相关的所有日志,包括启动服务、停止服务、等等
journalctl -b -t systemd

# 导出日志到文件
journalctl > all.log

0

评论区