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采用“并行启动”策略,利用服务间依赖关系图,实现多任务并发启动,显著提升系统启动速度。所有服务、挂载点、套接字等均抽象为 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=10s,StartLimitBurst=5)。 这两个选项虽然经常与 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。 |
我们最常使用的是 service 和 target 两种 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
评论区