systemd 是什么:工作原理以及如何管理 Linux 服务
systemd 是 Linux 的初始化系统和服务管理器:内核启动后它作为第一个进程(PID 1)运行,按正确顺序启动其他服务并负责监控它们。systemd 的所有功能都通过单元(unit)配置,即描述服务、定时器、挂载点等的文本文件;使用 systemctl 启动、停止和设置开机自启,使用 journalctl 查看日志。
systemd 是初始化系统和服务管理器,被大多数现代 Linux 发行版使用:Debian、Ubuntu、Fedora、RHEL 及其衍生版。内核在启动后以 PID 1 运行 systemd,然后由它启动网络、挂载磁盘、运行 Web 服务器、数据库以及其他所有服务。简单地说,systemd 就是一个“调度员”:它知道哪些服务应该运行,按什么顺序启动,并在服务崩溃时采取措施。
确认系统是否使用 systemd 很简单:
$ ps -p 1 -o comm=
systemd
为什么需要 systemd
在 Linux 早期,使用的是 SysV init:/etc/init.d/ 中的一组 shell 脚本,按顺序依次运行。这种方式容易理解,但速度慢且脆弱。每个脚本都要自己写 PID 文件,自己判断进程是否存活,而崩溃后的自动重启需要额外配置。
systemd 以集中的方式解决了这些问题:
- 并行启动。没有依赖关系的服务同时启动,因此开机更快。
- 依赖关系和顺序。服务描述中会明确写出它需要什么(例如网络),以及应该在什么之后启动。
- 监督。systemd 通过 cgroups 跟踪服务的所有进程,并能在服务失败时重启它。
- 统一的日志。所有服务的输出由 journald 收集,使用一条命令即可查看。
- 统一的接口。所有服务的管理方式一致:
systemctl start|stop|restart|status。
除了服务管理器,systemd 项目还包括一组组件:journald(日志)、logind(用户会话)、timesyncd(时间同步)、resolved(DNS)、networkd(网络)。并非每个发行版都使用全部组件,但作为 PID 1 的 systemd 和 journald 几乎随处可见。
单元:systemd 的组成部分
systemd 管理的所有对象都由单元(unit)描述。单元是 INI 格式的文本文件,单元类型由文件扩展名决定。
| 类型 | 扩展名 | 描述的内容 |
|---|---|---|
| service | .service | 服务:守护进程或一次性任务 |
| timer | .timer | 按计划启动另一个单元(替代 cron) |
| socket | .socket | 套接字,有连接访问时启动对应服务 |
| target | .target | 单元组,即“系统状态”(例如 multi-user.target) |
| mount | .mount | 挂载点 |
| path | .path | 监视文件或目录 |
单元文件的存放位置
单元文件保存在多个目录中,并且有优先级之分:
/usr/lib/systemd/system/(在 Debian 和 Ubuntu 中还有/lib/systemd/system/):由软件包安装的单元。不要手动修改它们,软件包更新时会覆盖你的改动。/etc/systemd/system/:管理员的单元和覆盖配置,优先级最高。/run/systemd/system/:运行期间临时创建的单元。
要查看单元的最终内容(包括所有覆盖配置),使用 systemctl cat nginx.service。
Target 取代运行级别
旧的 runlevel 已被 target 取代。multi-user.target 是普通的服务器模式,没有图形界面;graphical.target 带有图形桌面;rescue.target 是单用户恢复模式。使用 systemctl get-default 查看当前默认 target,使用 systemctl set-default multi-user.target 修改。
systemctl:管理服务
systemctl 是操作 systemd 的主要工具。大多数修改系统状态的命令都需要 sudo。
| 命令 | 作用 |
|---|---|
systemctl status nginx | 服务状态、PID、内存、最近的日志行 |
systemctl start nginx | 立即启动 |
systemctl stop nginx | 停止 |
systemctl restart nginx | 重启(stop + start) |
systemctl reload nginx | 在不停止的情况下重新读取配置(服务需要支持) |
systemctl enable nginx | 设置开机自启 |
systemctl disable nginx | 取消开机自启 |
systemctl enable --now nginx | 设置开机自启并立即启动 |
systemctl is-active nginx | 输出 active/inactive,便于在脚本中使用 |
systemctl list-units --type=service | 已加载的服务 |
systemctl --failed | 失败的单元 |
systemctl daemon-reload | 修改单元文件后重新读取 |
如何阅读 systemctl status
$ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-10-12 09:14:03 UTC; 2h 5min ago
Docs: man:nginx(8)
Main PID: 812 (nginx)
Tasks: 3 (limit: 2307)
Memory: 6.1M
CPU: 154ms
CGroup: /system.slice/nginx.service
├─812 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
├─813 "nginx: worker process"
└─814 "nginx: worker process"
“Loaded” 一行说明单元文件的来源以及是否启用了开机自启(enabled)。“Active” 一行表示当前状态:active (running)、inactive (dead) 或 failed。“CGroup” 部分列出了服务的全部进程,systemd 就是这样判断哪些进程属于该服务的。
enable 与 start 的区别
这是常见的混淆。start 立即启动服务,但重启后它不会自动运行。enable 会在目标目录中创建符号链接(例如 /etc/systemd/system/multi-user.target.wants/),使服务在开机时启动,但它不会立即运行。通常两者都需要,因此使用 enable --now。
还有第三种状态:mask。systemctl mask service 会把单元链接到 /dev/null,之后它既不能手动启动,也不能作为依赖被启动。当需要彻底禁用某个服务时,这很有用。
journalctl:服务日志
服务写入 stdout 和 stderr 的所有内容都会被 systemd-journald 捕获,并连同元数据(单元名称、PID、优先级、时间)保存到二进制日志中。使用 journalctl 读取日志。
journalctl -u nginx # 某个服务的日志
journalctl -u nginx -f # 实时跟踪,类似 tail -f
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx -n 50 # 最后 50 行
journalctl -b # 本次启动以来
journalctl -b -1 # 上一次启动
journalctl -p err -b # 仅错误及更严重的级别
journalctl -k # 内核消息
journalctl --disk-usage # 日志占用的空间
选项 -p 按 syslog 优先级过滤:emerg、alert、crit、err、warning、notice、info、debug。指定的级别会包含比它更严重的所有消息。
重启后保留日志
如果不存在 /var/log/journal 目录,并且 /etc/systemd/journald.conf 中设置了 Storage=auto,日志只保存在内存(/run/log/journal)中,重启后会丢失。要让日志永久保存,设置 Storage=persistent,或者创建该目录并重启 journald。日志大小由 SystemMaxUse= 限制;清理旧日志可以使用 journalctl --vacuum-time=2weeks 或 --vacuum-size=500M。
journald 经常与 rsyslog 配合使用:journald 把消息传给 rsyslog,后者写入传统的文本日志文件 /var/log/,并能把日志发送到中心服务器。
让自己的服务开机自启:unit 文件示例
假设你有一个应用(脚本或可执行文件),需要保持运行、开机启动,并在崩溃后重新启动。创建文件 /etc/systemd/system/myapp.service:
[Unit]
Description=My web application
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/bin/server --port 8080
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
各个段的含义如下:
- [Unit]:描述和依赖。
After=只设定顺序;Wants=是弱依赖(会尝试拉起该单元,但如果它没有启动成功,本单元不会失败)。强依赖使用Requires=。 - [Service]:如何启动。
Type=simple表示ExecStart中的进程就是服务本身,并且它不会转入后台。User=指定以非特权用户运行。Restart=on-failure表示服务因错误退出时,在RestartSec秒后重新启动。 - [Install]:
enable时执行的操作。WantedBy=multi-user.target表示“在普通开机时启动”。
应用这个配置:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
journalctl -u myapp -f
unit 文件中的常见错误
- ExecStart 中使用相对路径。程序路径必须是绝对路径。systemd 不会像 shell 那样在用户的
PATH中查找命令。 - 忘记 daemon-reload。修改文件后,systemd 仍会使用旧版本,并会在
status中给出提示。 - 守护进程转入后台。如果程序自己 fork,但配置的是
Type=simple,systemd 会认为服务已经退出。应让程序以前台模式运行,或者使用Type=forking加上PIDFile=。 - 依赖用户配置文件中的环境变量。服务看不到
~/.bashrc和/etc/profile。变量应通过Environment=或EnvironmentFile=设置。
不修改软件包单元的覆盖配置
如果想修改软件包中服务的参数,但不改动它的文件,可以使用 drop-in 覆盖配置:
sudo systemctl edit nginx
编辑器会打开,修改内容保存到 /etc/systemd/system/nginx.service.d/override.conf 中。例如,可以在其中添加 [Service] 段和 Restart=always。退出编辑器后,systemd 会自动重新读取配置。
用 systemd 定时器代替 cron
定时器是一种单元,它按计划启动同名的服务。与 cron 相比,它的优势是:任务日志进入 journal,可以声明依赖,错过的运行可以补上(Persistent=true)。下面是每天备份的示例:
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers
可以用 systemd-analyze calendar "*-*-* 03:00:00" 检查 OnCalendar 表达式,它会显示下一次运行的时间。
诊断启动耗时
systemd 记录了每个单元的启动时间,因此很容易分析开机慢的原因:
systemd-analyze # 总启动时间
systemd-analyze blame # 按启动耗时排序的单元
systemd-analyze critical-chain # 拖慢启动的关键链
systemd-analyze verify /etc/systemd/system/myapp.service # 检查单元文件
总结
systemd 是现代 Linux 中的 PID 1 和服务管理器。它用单元描述一切,通过 systemctl 管理,把日志收集到由 journalctl 读取的 journal 中,还可以用定时器代替 cron。要熟练使用它,只需掌握 status、enable --now、journalctl -u,并能编写带有 Restart=on-failure 的简单 unit 文件。systemd、journald 和日志管理的内容都会在 Linux 课程中结合实践讲解。
常见问题
怎样判断我的系统是否使用 systemd?
执行 ps -p 1 -o comm=,如果输出是 systemd,说明系统以 systemd 运行。此外,只有通过 systemd 启动时才会存在 /run/systemd/system 目录。
systemctl restart 和 reload 有什么区别?
restart 会完全停止再重新启动服务,连接会中断。reload 要求服务重新读取配置而不停止,但只有单元中定义了 ExecReload 时才可用。
普通用户能不用 sudo 运行 systemd 服务吗?
可以,systemd 提供用户实例:单元文件放在 ~/.config/systemd/user/ 中,使用 systemctl --user 管理。要让它们在没有登录会话时也能运行,需要为该用户执行 loginctl enable-linger。
服务处于 failed 状态时,原因是什么,怎样清除这个状态?
原因可以在 journalctl -u 名称 -b 和 status 输出的 Active 行中找到。修复后重新启动服务;如果只想清除 failed 标记而不重启,使用 systemctl reset-failed。
systemd 的日志保存在磁盘的什么位置?
持久化存储时位于 /var/log/journal/,采用二进制格式;否则位于内存中的 /run/log/journal/。应使用 journalctl 读取,而不是用文本工具打开。
课程“Linux: с нуля до уверенного администрирования”结合实践详细讲解了这一主题。
- systemd: как на самом деле запускаются сервисы в современном Linux
- Журналы и мониторинг: journald, rsyslog и sysstat