1. 从 SysVinit 到 Systemd:一次静默的系统管理变革
在当前的 Linux 发行版中,绝大多数(如 CentOS 7 及以上、Ubuntu 16.04 及以上、Debian 8 及以上、Fedora、Arch Linux 等)都已将 systemd 作为标准的初始化系统和服务管理器。在此之前的很长一段时间里,Linux 系统普遍采用 SysVinit 或 Upstart 来启动和管理服务,它们通常通过 /etc/init.d/ 下的脚本或 service 命令进行操作。
systemd 的设计目标是克服传统初始化系统的诸多局限,例如串行启动速度慢、脚本维护复杂、服务依赖管理不够清晰等问题。如今,理解 systemd 已经成为现代 Linux 运维的必备技能。不过,很多初学者容易混淆 systemd 和 systemctl 这两个概念,下面先做一个明确的区分:
- systemd:系统守护进程,PID=1,即 Linux 内核启动后运行的第一个用户态进程。它常驻后台,负责整个系统的启动、服务管理、进程监控、设备挂载、定时任务、日志收集以及电源管理等一系列核心功能。
- systemctl :管理员用来与 systemd 进行交互的命令行工具。无论是启停服务、设置开机自启,还是查看状态、分析依赖,都通过
systemctl命令完成。
简单地说,systemd 是管理者,systemctl 是管理工具。本文将系统介绍 systemd 的单元(Unit)体系、systemctl 的日常使用、自定义服务文件的编写,以及配套的日志、时间、主机名管理工具,帮助读者建立起完整的知识框架。
2. systemd 的核心概念:单元(Unit)体系
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 目录下创建符号链接,从而实现开机自启。
3. systemctl:管理员的核心操作接口
systemctl 是操作 systemd 单元的统一命令,其基本用法为:
systemctl [选项] 动作 [单元名称...]
下面按照典型的运维场景,分类列出常用命令。
3.1 服务生命周期管理
# 启动服务
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。
3.2 开启自启与状态查询
# 设置服务开机自动启动
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
3.3 单元管理进阶操作
# 重载 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,达到了彻底禁用的效果,常用于防止冲突的服务意外启动。
3.4 系统运行目标的切换
systemd 使用 target 来管理运行级别:
# 切换到多用户模式(命令行界面,类似传统的 runlevel 3)
systemctl isolate multi-user.target
# 切换到图形界面模式(类似传统 runlevel 5)
systemctl isolate graphical.target
# 设置系统默认启动目标
systemctl set-default multi-user.target
# 查看当前设置的默认目标
systemctl get-default
3.5 系统电源管理
# 关机
systemctl poweroff
# 重启
systemctl reboot
# 挂起
systemctl suspend
# 休眠
systemctl hibernate
4. 编写自定义 Service 文件
在运维工作中,经常需要将自行开发的程序或第三方软件注册为 systemd 服务,以便统一管理。一个标准的 service 文件由三个段落组成:[Unit]、[Service] 和 [Install]。
4.1 基本模板
创建文件 /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
4.2 段落详解
Unit 段落
Description:对服务的简要描述,会显示在systemctl status中。Documentation:可选的参考文档地址。After:定义启动顺序,表示本服务应在所列单元启动之后再启动,但不构成强依赖。Before:与After相反,声明本服务应在所列单元启动之前启动。Wants:弱依赖关系。期望所列单元启动,但如果它们启动失败,不会导致本服务失败。Requires:强依赖关系。如果任一所需单元启动失败,本服务也将被停止。BindsTo:比Requires更紧密的绑定,如果所绑定的单元停止,本服务也会被立刻停止。
Service 段落
-
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目录,增强安全性。
Install 段落
WantedBy:指定在哪个 target 下启用该服务。绝大多数系统服务都填写multi-user.target,表示在多用户命令行模式下自动启动。RequiredBy:与WantedBy类似但创建强依赖链接。
4.3 部署步骤
- 将编写好的
.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 服务的使用,可以参考我另外的一篇博客,这篇博客中就是封装了自定义服务。
5. 配套工具:构建完整的 systemd 工具链
systemd 提供了一系列与系统管理相关的命令行工具,它们都以 ctl 结尾,与 systemctl 风格一致,易学易用。
5.1 journalctl:集中式日志管理
在 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/ 目录下。
5.2 hostnamectl:主机名管理
# 查看当前主机名及相关信息(静态主机名、操作系统、内核等)
hostnamectl
# 设置系统主机名(同时更新静态、瞬态和美观名称)
hostnamectl set-hostname new-hostname
# 单独设置美观主机名(可包含空格和特殊字符)
hostnamectl set-hostname "Production Web Server" --pretty
该工具实际上修改的是 /etc/hostname 文件以及内核的瞬态主机名。
5.3 timedatectl:时间、日期、时区控制
# 查看当前系统时间、硬件时钟、时区和 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"
5.4 其他实用工具
-
systemd-analyze:启动性能分析工具。# 查看本次启动总耗时 systemd-analyze # 按耗时排序,列出每个服务的启动时间 systemd-analyze blame # 显示启动过程的关键路径(最长耗时链) systemd-analyze critical-chain -
loginctl:管理用户登录会话、座位等。 -
systemd-cgls/systemd-cgtop:查看 cgroup 层级结构及实时资源使用情况。
6. 最佳实践与故障排查思路
6.1 服务文件编写建议
- 最小权限原则 :通过
User=和Group=为服务指定专用的非特权账户,避免以 root 身份运行。 - 资源限制 :适当配置
LimitNOFILE、MemoryMax、CPUQuota等,防止服务过度消耗系统资源。 - 安全增强 :开启
PrivateTmp=true、ProtectSystem=full、NoNewPrivileges=true等指令,可有效提升隔离性。 - 明确的启动类型 :优先使用
Type=simple,若应用程序本身会 fork 到后台,则选择Type=forking并正确指定PIDFile。
6.2 常见问题排查方法
- 服务启动失败
- 执行
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服务。
- 检查
7. 结语
Systemd 并非单纯替换了旧的 init 系统,而是构建了一套统一、高效且可扩展的服务管理体系。对于刚步入职场的 Linux 学习者而言,熟练掌握 systemctl 操作、理解 Unit 的编写方法,以及善用 journalctl 等日志排查工具,是运维的必备技能喔。
本文为同步搬运内容,原创首发于个人独立博客网站:https://www.zheng-chang-ren.xyz
平台更新优先级说明:所有技术笔记、实验教程、踩坑总结均会优先发布、长期维护于个人独立博客;CSDN 仅作为辅助分发渠道。
若想查阅全部完整文集、获取最新首发内容,建议收藏并优先访问我的个人博客网站。