Linux systemd 服务管理:Unit 文件、Socket/Timer、依赖与资源控制

深入 Linux systemd 服务管理:Unit 文件详解(Service/Socket/Timer/Path)、systemctl 常用命令、依赖与启动顺序、资源限制(cgroup)、日志查看、编写自定义服务与开机自启。

引言

现代 Linux 发行版几乎都基于 systemd 管理服务——它是 PID 1(init 进程),负责拉起、监督、重启你的所有守护进程。本文讲透 systemd 服务管理:先给 Unit 文件全景(Service / Socket / Timer / Path 四大类型),再拆解 Service 单元的常用配置(依赖、重启策略、资源限制),接着讲 systemctl 的完整命令体系、Socket 激活与 Timer 定时任务,最后演示如何从零写一个开机自启的服务并排错。

前置:/linux-process-management/(进程与启动)。进程信号与后台概念见该文。容器视角见 [[docker]]。


目录


1. systemd 是什么:PID 1 与 Unit 体系

systemd = 第一个进程(PID 1)+ 服务管理器:

内核 → systemd(PID 1) → 并行拉起系统服务 → 开机自启应用
                        ↑
             监督每个服务,挂了自动重启

核心概念:Unit(单元)——一切被 systemd 管理的「东西」:

类型后缀管理什么
Service.service守护进程/服务
Socket.socket网络/IPC 端口
Timer.timer定时任务
Path.path文件/目录变化监控
Mount.mount挂载点
Target.target一组单元的「启动目标」

心智:systemd 把「服务、定时、端口、挂载」都抽象成 Unit——统一用 systemctl 管理,这是它统治现代 Linux 的原因。


2. Unit 文件四大类型

Unit 文件存放位置:

/usr/lib/systemd/system/    # 发行版安装的
/etc/systemd/system/        # 管理员自定义(优先)
/lib/systemd/system/        # 旧式软链

① Service(服务)——最常用:

# /etc/systemd/system/myapp.service
[Unit]
Description=My App Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/myapp --config /etc/myapp.conf
Restart=on-failure
User=myapp

[Install]
WantedBy=multi-user.target

② Socket(端口激活)——服务不用常驻,有人连接才启动:

# myapp.socket
[Socket]
ListenStream=8080

[Install]
WantedBy=sockets.target

③ Timer(定时)——cron 的 systemd 替代:

# backup.timer
[Unit]
Description=Daily backup

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

④ Path(路径监控)——文件变化触发:

# config.path
[Path]
PathModified=/etc/myapp/config.json
Unit=myapp-reload.service

记忆:Service 管常驻、Socket 管按需拉起、Timer 管定时、Path 管文件触发——四大类型覆盖守护进程的绝大多数场景。


3. Service 单元核心配置

[Service] 段核心参数:

参数含义示例
Type进程类型simple/forking/oneshot
ExecStart启动命令/usr/bin/myapp
ExecStartPre启动前钩子/bin/mkdir -p /var/run
Restart重启策略on-failure/always
RestartSec重启间隔2s
User/Group运行用户myapp
Environment环境变量KEY=value
EnvironmentFile环境文件/etc/myapp/env
WorkingDirectory工作目录/opt/myapp
LimitNOFILE文件句柄上限65535

Type 详解:

simple     — 主进程直接前台(推荐现代服务)
forking    — 启动后 fork 到后台(老式守护进程)
oneshot    — 一次性任务,执行完即退
notify     — 启动完成后通过 sd_notify 通知 systemd

Restart 策略:

Restart=on-failure   # 非正常退出才重启(推荐)
Restart=always       # 任何退出都重启(含正常)
RestartSec=2s        # 重启间隔,防崩溃风暴

记忆:Type=simple + Restart=on-failure 是现代服务的黄金组合——前台进程、异常自动拉起。


4. systemctl 命令体系

最常用的 systemctl 命令:

# 启动/停止/重启
systemctl start myapp
systemctl stop myapp
systemctl restart myapp
systemctl reload myapp        # 热加载配置(需服务支持)

# 开机自启
systemctl enable myapp         # 开机自启
systemctl disable myapp        # 取消
systemctl enable --now myapp   # 自启 + 立即启动

# 查看状态
systemctl status myapp         # 状态 + 最近日志
systemctl is-active myapp      # active/inactive/failed
systemctl is-enabled myapp     # enabled/disabled

# 列表
systemctl list-units --type=service --all
systemctl list-units --failed   # 查看失败服务

状态解读:

loaded  = 已加载 unit
active(running)  = 正在运行
active(exited)   = 已完成(oneshot)
failed  = 启动失败

记忆:enable 管「开机自启」、start 管「立即运行」——enable --now 两步合一,最常用。


5. 依赖与启动顺序:After/Wants/Requires

启动顺序(After/Before)与依赖(Requires/Wants)是两回事:

[Unit]
Description=Web app
# 顺序:network 先就绪
After=network-online.target postgresql.service
# 依赖:network 失败则不启动本服务
Requires=network-online.target
# 软依赖:postgresql 尽力拉起,失败不阻塞
Wants=postgresql.service
指令语义
After本服务在其后启动(顺序)
Before本服务在其前启动
Requires硬依赖:对方失败→本服务失败
Wants软依赖:对方失败→本服务照跑
BindsTo强绑定:对方停止→本服务也停

依赖方向的直觉:

数据库必须先于应用启动?→ After=db.service
数据库挂了应用必须停?  → BindsTo=db.service
数据库挂了应用照常跑?  → Wants=db.service

记忆:After 管先后,Wants/Requires 管生死——顺序用 After、软依赖用 Wants、硬依赖用 Requires,别混为一谈。


6. 资源控制与安全隔离

systemd 用 cgroup 给服务限资源:

[Service]
# CPU 与内存
CPUWeight=100            # 相对权重(100 默认)
MemoryMax=1G             # 内存硬上限
MemoryHigh=512M          # 软上限(压力下回收)
TasksMax=512             # 线程数上限

# IO
IOWeight=100

# 文件描述符
LimitNOFILE=65535

# 安全隔离
ProtectSystem=strict     # 只读保护系统目录
ProtectHome=true         # 隐藏家目录
PrivateTmp=true          # 独立临时目录
NoNewPrivileges=true     # 禁止提权
ReadOnlyPaths=/etc/myapp  # 只读特定目录

常用安全指令:

指令作用
ProtectSystem=strict系统目录只读
ProtectHome=true/home、/root 只读
PrivateTmp=true隔离 /tmp
NoNewPrivileges禁止 setuid 提权
CapabilityBoundingSet裁剪内核能力

记忆:systemd 是「零成本的容器」——不装 Docker 也能给服务限内存、隔离文件系统、防提权。


7. Timer 定时任务与 Socket 激活

Timer 取代 cron 的优势:可精确调度、可持久(错过补跑)、日志统一。

# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup at 02:30

[Timer]
OnCalendar=*-*-* 02:30:00   # 每天 02:30
Persistent=true              # 机器关机错过则开机补跑
RandomizedDelaySec=5m        # 随机延迟防风暴

[Install]
WantedBy=timers.target
# /etc/systemd/system/backup.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
systemctl enable backup.timer
systemctl list-timers         # 查看定时器

Socket 激活(按需启动):

# 监听 8080,有人连才拉起服务
systemctl start myapp.socket
ss -tlnp | grep 8080        # socket 监听中
# 服务本体 idle 状态,连接触发自动启动
方案适用
Timer定时任务(替代 cron)
Socket 激活低流量按需服务
Path文件变化触发

记忆:Timer + oneshot Service 是 cron 的现代替代——OnCalendar 精确调度、Persistent 错过补跑、日志统一走 journald。


8. 从零写一个开机自启服务

完整实战:让一个 Node/Python 应用开机自启:

# 1. 应用放 /opt/myapp,创建 systemd unit
sudo tee /etc/systemd/system/myapp.service > /dev/null <<'EOF'
[Unit]
Description=My API Service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Environment=NODE_ENV=production
EnvironmentFile=/etc/myapp/env.conf
Restart=on-failure
RestartSec=3s
MemoryMax=1G
ProtectSystem=strict
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
EOF

# 2. 创建运行用户(可选)
sudo useradd --system --shell /sbin/nologin myapp

# 3. 重新加载 + 启动 + 开机自启
sudo systemctl daemon-reload
sudo systemctl enable --now myapp

# 4. 验证
systemctl status myapp
curl localhost:8080/health

铁律:写完 unit 必须 daemon-reload——否则 systemd 不会识别新配置;改完 unit 同样要 reload。


9. 排错:journalctl 与 unit 状态

查看服务日志(journald):

journalctl -u myapp                    # 该服务的全部日志
journalctl -u myapp -f                 # 跟随实时
journalctl -u myapp --since today      # 今天
journalctl -u myapp --since "10 min ago"
journalctl -u myapp -p err             # 只看错误
journalctl -u myapp --no-pager -n 100  # 最后 100 行

排错流程:

1. systemctl status myapp      → 看状态 + 错误信息
2. journalctl -u myapp -p err  → 看日志
3. systemctl show myapp        → 查看生效配置
4. systemd-analyze blame       → 启动耗时分析
5. systemd-analyze verify unit → 校验 unit 语法

常见失败原因:

报错原因解法
Failed to startExecStart 路径/权限错检查命令可执行
(code=exited)进程退出非零看日志退出码
[FAILED] + 重启循环配置/依赖问题看 Restart 日志
Unit not found未 daemon-reloadreload 后再试

记忆:systemctl 看状态、journalctl 看日志——先 status 定位、再 journalctl 看细节,两步走完 80% 的排错。


10. 速查表

需求做法
启动/停止/重启systemctl start/stop/restart <svc>
开机自启systemctl enable --now <svc>
查看状态systemctl status <svc>
查看日志journalctl -u <svc>
跟随日志journalctl -u <svc> -f
定时任务Timer + oneshot Service
端口按需拉起Socket 激活
限内存MemoryMax=1G
防提权NoNewPrivileges=true
改配置生效systemctl daemon-reload
启动耗时systemd-analyze blame

一句话记忆:systemd 用 Unit 管理一切——Service 常驻、Socket 按需、Timer 定时、Path 触发;systemctl 管启停与自启、journalctl 管日志、daemon-reload 让配置生效;After 管顺序、Wants 软依赖、Requires 硬依赖;cgroup 限资源、Protect 隔离文件——一套 unit 吃透服务管理。


延伸阅读

  • /linux-process-management/ — 进程管理与启动
  • /linux-journald-logging/ — journald 日志深入
  • /linux-kernel-boot-process/ — systemd 启动目标
  • [[docker]] — cgroup/namespace 容器化
  • [[os]] — 操作系统原理

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「linux」更多文章

  1. Linux 高级文件系统:XFS、Btrfs、ZFS 与存储进阶
  2. Linux 高可用与负载均衡:HAProxy、Keepalived 与集群方案
  3. Linux 防火墙与 nftables:规则集、链、NAT 与网络安全防护