1. 什么是 Service / Daemon
在 Linux 里,Daemon(守护进程)是在后台持续运行、不与终端直接交互的进程。
- Daemon 这个词源自希腊神话中介于神与人之间的精灵,寓意"在背后默默守护"。Unix 沿用了该命名,进程名通常以字母
d结尾,如负责 SSH 连接的sshd、负责 Web 服务的httpd、以及 Linux 现代 init 系统本身的systemd。 - 一个典型 daemon 的生命周期是:系统启动时由 init 系统拉起,父进程为 PID 1,与任何控制终端完全脱离,然后在后台长期运行,直到被显式停止或系统关机。也有部分 daemon 采用"按需激活"的方式,平时不占资源,有请求时才被唤醒(如 socket activation)。
Service 则是对Daemon的抽象封装。在现代 Linux 中,service 指由 init 系统(即 systemd)统一管理的、具有完整生命周期控制的程序单元——启动、停止、重启、开机自启、依赖排序,都由 systemd 负责。
- 可以这样理解:daemon 是进程层面的概念,service 是管理层面的概念,两者通常指向同一个东西,但角度不同。
2. systemd 架构简述
systemd 是目前几乎所有主流 Linux 发行版的默认 init 系统,以 PID 1 的身份运行,是系统中第一个启动、最后一个退出的进程,负责统筹整个用户空间的初始化。
systemd 的职责不止"启动服务",还是一套完整的系统管理框架:
PID 1: systemd
├── unit 文件解析 & 依赖图构建 # 读取 .service/.target 等配置,构建启动顺序
├── 并行启动 # 依赖满足即立即启动,不串行等待
├── cgroup 隔离 # 每个服务独立 cgroup,精准追踪子进程、限制资源
├── journal 日志收集 # 统一收集所有服务的 stdout/stderr,替代分散的日志文件
└── socket/dbus/timer activation # 按需激活:有连接才启动服务,有时间才触发任务
管理的基本单元叫做 Unit,不同类型的 unit 对应不同的系统资源,常用的有:
| 类型 | 后缀 | 用途 |
|---|---|---|
| Service | .service |
管理一个进程/守护进程的完整生命周期 |
| Target | .target |
同步点,如 multi-user.target |
| Timer | .timer |
定时触发,是 cron 的现代替代 |
| Socket | .socket |
socket 激活,有连接时才拉起对应 service |
| Mount | .mount |
管理文件系统挂载点 |
日常打交道最多的是 .service,Alist、Clash 等软件的配置文件都属于 Service。
3. Service Unit 文件结构
[Unit]
Description=服务描述
Documentation=https://... # 可选文档链接
Wants=network-online.target # 弱依赖:目标最好存在,失败不阻止本服务
Requires=xxx.service # 强依赖:目标失败则本服务也失败
After=network-online.target # 顺序:在目标之后启动(不隐含依赖)
Before=xxx.service # 顺序:在目标之前启动
[Service]
Type=simple # 见下方 Type 说明
ExecStart=/usr/bin/xxx --args
ExecStop=/usr/bin/xxx stop # 可选,默认 SIGTERM
ExecReload=/bin/kill -HUP $MAINPID
WorkingDirectory=/path/to/dir
User=nobody
Group=nobody
Environment=VAR=value
EnvironmentFile=/etc/xxx.env
Restart=on-failure # 见下方 Restart 说明
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target # 系统服务挂载点
# WantedBy=default.target # 用户服务挂载点
Type 详解
| Type | 行为 | 适用场景 |
|---|---|---|
simple |
ExecStart 进程即主进程,启动即视为就绪 | 大多数服务 |
exec |
等待 exec() 成功后才视为就绪(比 simple 更严格) | 需要确认执行的场景 |
forking |
父进程 fork 后退出,子进程成为 daemon | 传统 SysV daemon |
oneshot |
进程退出后视为完成,适合脚本 | 初始化脚本 |
notify |
进程主动通过 sd_notify() 通知就绪 |
支持 systemd 协议的服务 |
dbus |
通过 D-Bus 名称确认就绪 | D-Bus 服务 |
Restart 策略
| 值 | 触发重启的条件 |
|---|---|
no |
从不(默认) |
always |
任何退出(含正常退出) |
on-failure |
非零退出码、信号异常终止 |
on-abnormal |
信号/超时,不含正常退出 |
on-success |
仅正常退出(exit 0) |
4. 系统服务 vs 用户服务
| 系统服务 | 用户服务 | |
|---|---|---|
| Unit 路径 | /etc/systemd/system/(管理员) /usr/lib/systemd/system/(软件包) |
~/.config/systemd/user/ |
| 运行身份 | root 或指定 User | 当前登录用户 |
| 启动时机 | 系统引导时 | 用户登录后 |
| 管理命令 | systemctl <cmd> |
systemctl --user <cmd> |
| WantedBy | multi-user.target |
default.target |
| 日志查看 | journalctl -u xxx |
journalctl --user -u xxx |
| lingering | — | 启用后可在无用户登录时运行 |
启用 lingering(用户服务开机自启,无需登录):
loginctl enable-linger $USER
5. systemctl 常用命令速查
服务管理
# 启动 / 停止 / 重启 / 重载配置
systemctl start <unit>
systemctl stop <unit>
systemctl restart <unit>
systemctl reload <unit> # 重载服务自身配置(不重启进程)
# 开机自启
systemctl enable <unit> # 创建 symlink
systemctl disable <unit> # 删除 symlink
systemctl enable --now <unit> # enable + 立即启动
# 状态查看
systemctl status <unit> # 详情 + 近期日志
systemctl is-active <unit> # active / inactive
systemctl is-enabled <unit> # enabled / disabled
# 重载 unit 文件(修改后必须执行)
systemctl daemon-reload
系统级操作
以下命令可加 --user 来查看用户服务
systemctl list-units --type=service # 列出所有 service
systemctl list-units --type=service --failed # 列出失败的
systemctl list-unit-files # 列出所有 unit 文件及启用状态
systemctl cat <unit> # 查看 unit 文件内容
systemctl edit <unit> # 创建 drop-in 覆盖文件
systemctl show <unit> # 显示所有属性(机器可读)
用户服务(加 --user)
systemctl --user start alist.service
systemctl --user enable alist.service
systemctl --user status alist.service
systemctl --user daemon-reload
journalctl --user -u alist.service -f
6. 日志:journalctl
journalctl -u <unit> # 查看某服务全部日志
journalctl -u <unit> -f # 实时跟踪(tail -f 效果)
journalctl -u <unit> -n 50 # 最近 50 行
journalctl -u <unit> --since "1 hour ago"
journalctl -u <unit> -p err # 仅 error 级别以上
journalctl --user -u <unit> # 用户服务日志
journalctl -b # 本次启动日志
journalctl -b -1 # 上次启动日志
7. 实例分析
实例 1:Alist 用户服务
# ~/.config/systemd/user/alist.service
[Unit]
Description=Alist User Service
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/.config/alist # %h = $HOME
ExecStart=/usr/bin/alist server --data %h/.config/alist/data
Restart=on-failure
[Install]
WantedBy=default.target
要点解读:
- 用户服务:放在
~/.config/systemd/user/,WantedBy=default.target %h是 systemd specifier,展开为用户家目录(等价于$HOME)Wants=+After=组合:等待网络就绪,但网络失败不阻止启动Restart=on-failure:崩溃自动重启,正常退出不重启
部署流程:
systemctl --user daemon-reload
systemctl --user enable --now alist.service
systemctl --user status alist.service
实例 2:Clash Verge 系统服务
# /etc/systemd/system/clash-verge-service.service
[Unit]
Description=Clash Verge Service helps to launch Clash Core.
After=network-online.target nftables.service iptables.service
[Service]
Type=simple
ExecStart=/usr/bin/clash-verge-service
Group=wenmou # 以指定用户组运行(特权操作需要)
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
要点解读:
- 系统服务:放在
/etc/systemd/system/,WantedBy=multi-user.target After=列出多个依赖:等防火墙规则(nftables/iptables)加载后再启动,避免代理规则冲突Group=wenmou:不以 root 运行,但保留组权限(用于 tun 设备、网络配置等)Restart=always:无论何种原因退出都重启,配合RestartSec=5防止重启风暴- 注意:未设置
User=,意味着以 root 用户 + wenmou 组运行
部署流程:
sudo systemctl daemon-reload
sudo systemctl enable --now clash-verge-service.service
sudo systemctl status clash-verge-service.service
8. 常用 Specifiers(Unit 文件变量)
| Specifier | 含义 |
|---|---|
%h |
用户家目录($HOME) |
%u |
用户名 |
%U |
用户 UID |
%n |
unit 完整名称 |
%i |
实例名(模板 unit @ 后的部分) |
%t |
Runtime 目录(/run 或 $XDG_RUNTIME_DIR) |
9. 常见问题排查
# 忘记服务名
systemctl list-units --type=service
systemctl --user list-units --type=service
# 服务启动失败,查详情
systemctl status <unit>
journalctl -u <unit> -n 100 --no-pager
# 修改 unit 文件后忘记 daemon-reload
systemctl daemon-reload
# 查看服务实际执行的完整命令(specifier 展开后)
systemctl show <unit> -p ExecStart
# 用户服务无法开机自启(未登录时不运行)
loginctl enable-linger $USER
# 检查 unit 文件语法
systemd-analyze verify ~/.config/systemd/user/alist.service
10. 最佳实践
- 修改 unit 文件后必须
daemon-reload,否则 systemd 仍使用旧配置 - 优先用用户服务运行个人软件(Alist 等),避免不必要的 root 权限
enable --now一步完成开机自启 + 立即启动- 不要直接编辑
/usr/lib/systemd/system/下的文件,应在/etc/systemd/system/创建同名文件覆盖 Restart=on-failure+RestartSec=5是生产环境的常见组合,避免崩溃循环- 日志优先用
journalctl -u <unit> -f实时观察启动过程