基础

systemd 是什么:工作原理以及如何管理 Linux 服务

更新于: · Skillaria.Top

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: с нуля до уверенного администрирования”结合实践详细讲解了这一主题。

前往课程