在当前的 Linux 发行版中,绝大多数(如 CentOS 7 及以上、Ubuntu 16.04 及以上、Debian 8 及以上、Fedora、Arch Linux 等)都已将 systemd 作为标准的初始化系统和服务管理器。在此之前的很长一段时间里,Linux 系统普遍采用 SysVinit 或 Upstart 来启动和管理服务,它们通常通过 /etc/init.d/ 下的脚本或 service 命令进行操作。
systemd 的设计目标是克服传统初始化系统的诸多局限,例如串行启动速度慢、脚本维护复杂、服务依赖管理不够清晰等问题。如今,理解 systemd 已经成为现代 Linux 运维的必备技能。不过,很多初学者容易混淆 systemd 和 systemctl 这两个概念,下面先做一个明确的区分:
systemctl 命令完成。简单地说,systemd 是管理者,systemctl 是管理工具。本文将系统介绍 systemd 的单元(Unit)体系、systemctl 的日常使用、自定义服务文件的编写,以及配套的日志、时间、主机名管理工具,帮助读者建立起完整的知识框架。
systemd 将系统中一切可管理的资源抽象为 Unit(单元),并采用统一的方式对其进行配置与监控。这种设计的好处是,无论是要管理一个后台服务、一个挂载点,还是一个定时任务,都可以用相同的语法和命令来操作。
每个 Unit 通过文件后缀来区分类型,常见的单元类型如下表所示:
| 后缀 | 单元类型 | 用途说明 |
|---|---|---|
.service | 服务单元 | 最常用的类型,用于定义后台守护进程,如 nginx、mysql、sshd 等。 |
.socket | 套接字单元 | 监听网络套接字或 IPC 通道,可配合 service 实现按需启动。 |
.mount | 挂载单元 | 管理系统挂载点,可代替传统的 /etc/fstab 配置。 |
.automount | 自动挂载单元 | 当某个目录被访问时自动触发挂载操作,常用于网络文件系统。 |
.target | 目标单元 | 用于分组管理其他单元,相当于传统运行级别的概念(如 multi-user.target)。 |
.device | 设备单元 | 由 udev 根据内核检测到的硬件设备动态创建。 |
.swap | 交换分区单元 | 管理 swap 分区或 swap 文件。 |
.timer | 定时器单元 | 定义基于时间或事件的任务触发,可替代传统的 cron 任务。 |
.path | 路径监控单元 | 监控文件或目录的变化,一旦事件发生便启动关联的服务。 |
这些单元的定义文件主要存放在两个目录:
/usr/lib/systemd/system/:系统自带的单元文件,由发行版包管理器维护。/etc/systemd/system/:管理员自定义的单元文件,具有更高优先级,可以覆盖系统默认配置。当使用 systemctl enable 命令时,实际上是在 /etc/systemd/system/ 的对应 target 目录下创建符号链接,从而实现开机自启。
systemctl 是操作 systemd 单元的统一命令,其基本用法为:
展开代码systemctl [选项] 动作 [单元名称...]
下面按照典型的运维场景,分类列出常用命令。
展开代码# 启动服务 systemctl start nginx.service # 停止服务 systemctl stop nginx.service # 重启服务 systemctl restart nginx.service # 重新加载配置(服务必须支持重载操作) systemctl reload nginx.service # 优雅地重新加载或重启(如果服务支持) systemctl try-restart nginx.service # 杀死服务进程(默认发送 SIGTERM,可指定信号) systemctl kill -s SIGKILL nginx.service
提示:当单元类型为
.service时,命令中的后缀通常可以省略,systemctl start nginx等价于systemctl start nginx.service。
展开代码# 设置服务开机自动启动 systemctl enable nginx.service # 禁止服务开机自启 systemctl disable nginx.service # 查看服务当前状态(活跃/非活跃、PID、内存占用、最近日志) systemctl status nginx.service # 确认某个服务是否被设为开机启动 systemctl is-enabled nginx.service # 列出所有处于活跃(运行)状态的服务单元 systemctl list-units --type=service --state=running # 列出所有已安装的服务单元文件(无论是否激活) systemctl list-unit-files --type=service # 查看服务的依赖关系树 systemctl list-dependencies nginx.service
展开代码# 重载 systemd 配置,新增或修改了 unit 文件后必须执行 systemctl daemon-reload # 编辑某个现有单元(会自动创建覆盖文件,位于 /etc/systemd/system/ 下) systemctl edit nginx.service # 查看单元的完整配置内容(包括覆盖部分) systemctl cat nginx.service # 显示单元的所有底层属性 systemctl show nginx.service # 屏蔽某个服务(使其无法被手动或自动启动) systemctl mask nginx.service # 取消屏蔽 systemctl unmask nginx.service
mask 命令会将单元文件符号链接至 /dev/null,达到了彻底禁用的效果,常用于防止冲突的服务意外启动。
systemd 使用 target 来管理运行级别:
展开代码# 切换到多用户模式(命令行界面,类似传统的 runlevel 3) systemctl isolate multi-user.target # 切换到图形界面模式(类似传统 runlevel 5) systemctl isolate graphical.target # 设置系统默认启动目标 systemctl set-default multi-user.target # 查看当前设置的默认目标 systemctl get-default
展开代码# 关机 systemctl poweroff # 重启 systemctl reboot # 挂起 systemctl suspend # 休眠 systemctl hibernate
在运维工作中,经常需要将自行开发的程序或第三方软件注册为 systemd 服务,以便统一管理。一个标准的 service 文件由三个段落组成:[Unit]、[Service] 和 [Install]。
创建文件 /etc/systemd/system/demo.service,内容如下:
展开代码ini[Unit] Description=Demo Application Service Documentation=https://docs.example.com After=network.target remote-fs.target Wants=network-online.target [Service] Type=simple User=appuser Group=appuser WorkingDirectory=/opt/demo ExecStart=/usr/bin/python3 /opt/demo/app.py --port=8080 ExecStop=/bin/kill -s QUIT $MAINPID Restart=on-failure RestartSec=5s Environment="APP_ENV=production" LimitNOFILE=65536 PrivateTmp=true [Install] WantedBy=multi-user.target
Description:对服务的简要描述,会显示在 systemctl status 中。Documentation:可选的参考文档地址。After:定义启动顺序,表示本服务应在所列单元启动之后再启动,但不构成强依赖。Before:与 After 相反,声明本服务应在所列单元启动之前启动。Wants:弱依赖关系。期望所列单元启动,但如果它们启动失败,不会导致本服务失败。Requires:强依赖关系。如果任一所需单元启动失败,本服务也将被停止。BindsTo:比 Requires 更紧密的绑定,如果所绑定的单元停止,本服务也会被立刻停止。Type:启动类型,直接影响 systemd 如何判断服务是否启动成功。
simple(默认):ExecStart 启动的进程即为服务的主进程,意味着 systemd 会立即认为服务已经启动。forking:服务自身调用 fork() 后父进程退出,子进程成为守护进程。此时通常需要配合 PIDFile 指定 PID 文件路径。oneshot:执行一次任务即退出,通常需要结合 RemainAfterExit=yes 以便 systemd 认为该服务仍然处于“活跃”状态。notify:服务在启动完成后会通过 sd_notify 发送通知,systemd 收到通知后才会认为服务启动完成。idle:类似 simple,但会等待所有任务(jobs)分派完毕后才启动服务。ExecStart:必填项,定义启动服务时所执行的命令及其参数。
ExecStop:自定义停止命令。若不指定,systemd 会先发送 SIGTERM,等待超时后发送 SIGKILL。
ExecReload:自定义重载配置命令(配合 systemctl reload 使用)。
Restart:退出后重启策略。
no(默认):不自动重启。always:无论退出码如何,都重启。on-success:仅当退出码为 0 时重启。on-failure:当退出码非 0 或进程被信号终止时重启。RestartSec:重启前的等待时间,默认 100ms,可设为 5s、1min 等。
User / Group:指定服务运行时的用户和组,强烈建议不要使用 root 运行服务。
WorkingDirectory:服务的工作目录。
Environment:设置环境变量,可多次使用。
LimitNOFILE:限制打开文件描述符的最大数量。
PrivateTmp:设为 true 将给服务分配独立的 /tmp 目录,增强安全性。
WantedBy:指定在哪个 target 下启用该服务。绝大多数系统服务都填写 multi-user.target,表示在多用户命令行模式下自动启动。RequiredBy:与 WantedBy 类似但创建强依赖链接。.service 文件放置到 /etc/systemd/system/ 目录下。systemctl daemon-reload 重载 systemd 配置。systemctl start demo.service。systemctl status demo.service。systemctl enable demo.service。修改 unit 文件后,也需要执行 systemctl daemon-reload 才能生效。
关于自定义 Service 服务的使用,可以参考我另外的一篇博客,这篇博客中就是封装了自定义服务。
systemd 提供了一系列与系统管理相关的命令行工具,它们都以 ctl 结尾,与 systemctl 风格一致,易学易用。
在 systemd 的架构中,所有由服务输出的标准输出、标准错误以及 syslog 消息,都被收集到统一的二进制日志系统(journal)中。journalctl 是用来访问和筛选这些日志的工具。
展开代码# 查看全部日志(默认从最新的条目开始) journalctl # 查看某个服务的所有日志 journalctl -u nginx.service # 实时跟踪日志(类似 tail -f) journalctl -fu nginx.service # 按时间范围过滤 journalctl --since "2025-06-01" --until "2025-06-02" journalctl --since "1 hour ago" # 只显示错误或更严重的日志(级别 err 及以下) journalctl -p err # 查看本次系统启动以来的日志 journalctl -b # 查看上一次启动的日志(在排查崩溃问题时非常有用) journalctl -b -1 # 按用户 ID、可执行文件路径等高级条件过滤 journalctl _UID=1000 journalctl _EXE=/usr/bin/sshd
默认情况下,journal 日志存储在内存中,重启后会丢失。若需持久化存储,应修改 /etc/systemd/journald.conf:
展开代码Storage=persistent
然后重启日志服务:systemctl restart systemd-journald。持久化日志会保存在 /var/log/journal/ 目录下。
展开代码# 查看当前主机名及相关信息(静态主机名、操作系统、内核等) hostnamectl # 设置系统主机名(同时更新静态、瞬态和美观名称) hostnamectl set-hostname new-hostname # 单独设置美观主机名(可包含空格和特殊字符) hostnamectl set-hostname "Production Web Server" --pretty
该工具实际上修改的是 /etc/hostname 文件以及内核的瞬态主机名。
展开代码# 查看当前系统时间、硬件时钟、时区和 NTP 同步状态 timedatectl # 列出所有可用时区 timedatectl list-timezones # 设置时区(例如中国标准时间) timedatectl set-timezone Asia/Shanghai # 启用或禁用网络时间同步(通常需要配合 chrony 或 systemd-timesyncd) timedatectl set-ntp true # 手动设置日期和时间(需要先关闭 NTP 同步) timedatectl set-time "2026-12-31 23:59:59"
systemd-analyze:启动性能分析工具。
展开代码# 查看本次启动总耗时 systemd-analyze # 按耗时排序,列出每个服务的启动时间 systemd-analyze blame # 显示启动过程的关键路径(最长耗时链) systemd-analyze critical-chain
loginctl:管理用户登录会话、座位等。
systemd-cgls / systemd-cgtop:查看 cgroup 层级结构及实时资源使用情况。
User= 和 Group= 为服务指定专用的非特权账户,避免以 root 身份运行。LimitNOFILE、MemoryMax、CPUQuota 等,防止服务过度消耗系统资源。PrivateTmp=true、ProtectSystem=full、NoNewPrivileges=true 等指令,可有效提升隔离性。Type=simple,若应用程序本身会 fork 到后台,则选择 Type=forking 并正确指定 PIDFile。systemctl status <服务名>,输出的最后几行通常会给出错误信息。journalctl -xe -u <服务名>。systemctl daemon-reload,再重新启动服务。systemctl enable <服务名>。systemctl is-enabled <服务名> 检查状态。[Install] 段中的 WantedBy 指向正确的 target。systemctl list-dependencies <服务名> 分析依赖树。After / Before 以及 Requires / Wants 配置是否符合实际需求。/etc/systemd/journald.conf 中的 Storage 选项,确保已设为 persistent 并重启了 systemd-journald 服务。Systemd 并非单纯替换了旧的 init 系统,而是构建了一套统一、高效且可扩展的服务管理体系。对于刚步入职场的 Linux 学习者而言,熟练掌握 systemctl 操作、理解 Unit 的编写方法,以及善用 journalctl 等日志排查工具,是运维的必备技能喔。
本文作者:zzz
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!