systemd 教程 / 用户服务(systemd --user)
用户服务(systemd –user)
概述
systemd 不仅管理系统级服务,还支持用户级服务。用户服务以普通用户身份运行,单元文件存放在用户主目录下,通过 systemctl --user 命令管理。用户服务适用于开发环境、个人定时任务、桌面应用等场景,无需 root 权限即可配置和管理。
基本概念
用户服务 vs 系统服务
| 对比项 | 系统服务 | 用户服务 |
|---|---|---|
| 运行身份 | root 或指定用户 | 当前用户 |
| 单元路径 | /etc/systemd/system/ | ~/.config/systemd/user/ |
| 管理命令 | sudo systemctl ... | systemctl --user ... |
| 日志查看 | journalctl -u ... | journalctl --user -u ... |
| 开机启动 | systemctl enable | systemctl --user enable |
| 运行时目录 | /run/ | $XDG_RUNTIME_DIR |
| 默认限制 | 较高 | 较低 |
用户服务的生命周期
用户登录 ──▶ systemd --user 启动 ──▶ 用户服务运行
│
用户注销 ──▶ systemd --user 退出 ──▶ 用户服务停止
│
linger 启用时 ──────────▶ 用户服务继续运行
用户 Unit 路径
搜索路径
用户服务的单元文件按以下路径搜索(优先级从高到低):
| 路径 | 说明 |
|---|---|
~/.config/systemd/user/ | 用户自定义单元(推荐) |
/etc/systemd/user/ | 系统管理员配置的用户单元 |
~/.local/share/systemd/user/ | 应用安装的用户单元 |
/usr/lib/systemd/user/ | 发行版提供的用户单元 |
创建用户服务目录
# 创建用户服务目录
mkdir -p ~/.config/systemd/user/
# 查看用户服务搜索路径
systemctl --user show -p UnitPath
# 查看所有用户单元路径
systemd-path user-unit
systemctl –user 命令
基本操作
# 查看所有用户服务
systemctl --user list-units --type=service
# 查看所有用户单元文件
systemctl --user list-unit-files
# 启动/停止/重启服务
systemctl --user start myapp.service
systemctl --user stop myapp.service
systemctl --user restart myapp.service
# 查看服务状态
systemctl --user status myapp.service
# 启用/禁用开机启动
systemctl --user enable myapp.service
systemctl --user disable myapp.service
# 查看用户服务日志
journalctl --user -u myapp.service
# 重新加载用户单元配置
systemctl --user daemon-reload
# 查看用户管理器状态
systemctl --user status
用户服务环境变量
用户服务的环境变量来源:
| 来源 | 说明 |
|---|---|
~/.config/environment.d/*.conf | 用户级环境变量文件 |
~/.pam_environment | PAM 环境变量(已弃用) |
~/.bash_profile | 不被用户服务读取 |
environment.d 配置
# ~/.config/environment.d/myenv.conf
# 每行一个 KEY=VALUE
EDITOR=vim
GOPATH=/home/user/go
NODE_ENV=development
# 应用环境变量
systemctl --user daemon-reexec
# 验证
systemctl --user show-environment
⚠️ 注意:用户服务不会读取 ~/.bashrc 或 ~/.bash_profile,需要通过 environment.d 设置环境变量。
linger 机制
什么是 linger?
默认情况下,用户的所有服务在用户注销后会停止。启用 linger 后,用户服务可以在用户无登录会话时持续运行。
启用 linger
# 为当前用户启用 linger
loginctl enable-linger
# 为特定用户启用 linger
sudo loginctl enable-linger username
# 禁用 linger
loginctl disable-linger
# 查看 linger 状态
loginctl show-user username -p Linger
检查 linger
# 查看所有启用了 linger 的用户
ls /var/lib/systemd/linger/
# 查看当前用户的 linger 状态
loginctl show-user $USER -p Linger
💡 提示:如果需要在服务器上运行用户服务(如开发服务器、数据库),必须启用 linger,否则在 SSH 断开后服务会停止。
linger 与 SSH 会话
场景 1:无 linger
SSH 登录 ──▶ 启动服务 ──▶ SSH 断开 ──▶ 服务停止
场景 2:有 linger
SSH 登录 ──▶ 启动服务 ──▶ SSH 断开 ──▶ 服务继续运行
实际案例
案例 1:用户定时任务
使用用户定时器替代 crontab:
# ~/.config/systemd/user/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=1800
[Install]
WantedBy=timers.target
# ~/.config/systemd/user/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/home/%u/scripts/backup.sh
StandardOutput=journal
StandardError=journal
# 启用定时器
systemctl --user enable --now backup.timer
# 查看定时器状态
systemctl --user list-timers
# 手动触发
systemctl --user start backup.service
案例 2:开发环境服务
运行本地开发服务器:
# ~/.config/systemd/user/dev-server.service
[Unit]
Description=Development Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/%u/projects/myapp
Environment=NODE_ENV=development
Environment=PORT=3000
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=default.target
# 启用并启动
systemctl --user enable --now dev-server.service
# 查看日志
journalctl --user -fu dev-server.service
# 查看状态
systemctl --user status dev-server.service
案例 3:用户级 Redis
# ~/.config/systemd/user/redis-dev.service
[Unit]
Description=User Redis Server
[Service]
Type=notify
ExecStart=/usr/bin/redis-server --port 6380 --daemonize no
ExecStop=/usr/bin/redis-cli -p 6380 shutdown
Restart=on-failure
RestartSec=3s
[Install]
WantedBy=default.target
案例 4:文件监控自动处理
# ~/.config/systemd/user/watch-downloads.path
[Unit]
Description=Watch Downloads Directory
[Path]
DirectoryNotEmpty=%h/Downloads
Unit=process-downloads.service
[Install]
WantedBy=default.target
# ~/.config/systemd/user/process-downloads.service
[Unit]
Description=Process Downloaded Files
[Service]
Type=oneshot
ExecStart=/home/%u/scripts/process-downloads.sh
systemctl --user enable --now watch-downloads.path
用户服务的 Specifier
用户服务中可以使用以下特殊占位符:
| Specifier | 说明 | 示例值 |
|---|---|---|
%u | 用户名 | john |
%U | 用户 ID | 1000 |
%h | 用户主目录 | /home/john |
%t | 运行时目录 | /run/user/1000 |
[Service]
WorkingDirectory=%h/projects
ExecStart=%h/bin/myapp
RuntimeDirectory=myapp
XDG 规范与用户服务
XDG Base Directory
用户服务遵循 XDG Base Directory 规范:
| 变量 | 默认值 | 用途 |
|---|---|---|
XDG_CONFIG_HOME | ~/.config | 配置文件 |
XDG_DATA_HOME | ~/.local/share | 数据文件 |
XDG_RUNTIME_DIR | /run/user/$UID | 运行时文件 |
XDG_CACHE_HOME | ~/.cache | 缓存文件 |
RuntimeDirectory
systemd 提供 RuntimeDirectory 指令,自动在 $XDG_RUNTIME_DIR 下创建目录:
[Service]
ExecStart=/opt/myapp/bin/server
RuntimeDirectory=myapp
RuntimeDirectoryMode=0755
# 等价于创建 /run/user/1000/myapp
⚠️ 注意:$XDG_RUNTIME_DIR(通常为 /run/user/$UID)在用户注销后会被清理(除非启用 linger)。
用户服务资源限制
内置限制
用户服务有默认的资源限制:
# 查看当前用户的默认限制
systemctl --user show -p DefaultMemoryMax
systemctl --user show -p DefaultTasksMax
# 查看用户管理器限制
systemctl --user show user@$(id -u).service -p MemoryMax,MemoryHigh,TasksMax
修改限制
# 临时修改(重启后失效)
systemctl --user set-property myapp.service MemoryMax=2G
# 永久修改(使用 drop-in 文件)
mkdir -p ~/.config/systemd/user/myapp.service.d/
cat > ~/.config/systemd/user/myapp.service.d/override.conf << 'EOF'
[Service]
MemoryMax=2G
CPUQuota=200%
EOF
# 重新加载
systemctl --user daemon-reload
增加用户服务默认限制
# 系统管理员可以修改用户服务默认限制
sudo loginctl set-property username MemoryMax=8G
sudo loginctl set-property username TasksMax=4096
# 或者修改 /etc/systemd/logind.conf
# [Login]
# UserTasksMax=4096
PAM 与用户服务
PAM 配置
用户服务的启动与 PAM(Pluggable Authentication Modules)密切相关:
用户登录 → PAM session → pam_systemd.so → 启动 systemd --user
查看用户会话
# 查看当前用户会话
loginctl list-sessions
# 查看会话详情
loginctl show-session $(loginctl | grep $USER | awk '{print $1}')
/etc/systemd/logind.conf 关键配置
[Login]
# 用户最大任务数
UserTasksMax=33%
# 用户会话超时(秒)
StopIdleSessionSec=infinity
# 是否在所有会话结束时杀死用户进程
KillUserProcesses=no
⚠️ 注意:KillUserProcesses=no 是大多数发行版的默认设置,确保用户进程在注销后继续运行。但某些发行版(如 Ubuntu)可能默认设为 yes。
调试用户服务
常用调试命令
# 查看用户管理器状态
systemctl --user status
# 查看用户服务日志
journalctl --user -u myapp.service
journalctl --user -fu myapp.service
# 查看用户服务配置
systemctl --user cat myapp.service
# 查看用户服务依赖
systemctl --user list-dependencies myapp.service
# 查看用户环境变量
systemctl --user show-environment
# 进入用户管理器的调试 shell
systemctl --user shell
常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 用户服务在注销后停止 | 未启用 linger | loginctl enable-linger |
| 服务启动失败 | 环境变量缺失 | 使用 environment.d 配置 |
$DISPLAY 或 $WAYLAND_DISPLAY 未设置 | 图形会话未正确传递 | 在服务中使用 Environment= |
Failed to connect to bus | D-Bus 会话不可用 | 检查 $DBUS_SESSION_BUS_ADDRESS |
| 用户管理器未运行 | 用户未登录 | 启用 linger 或手动启动 systemd --user |
手动启动用户管理器
# 如果用户管理器未运行,手动启动
systemctl --user daemon-reexec
# 或通过 lingering 触发
sudo loginctl enable-linger $USER
实用命令汇总
# 服务管理
systemctl --user start myapp.service
systemctl --user stop myapp.service
systemctl --user restart myapp.service
systemctl --user status myapp.service
systemctl --user enable myapp.service
systemctl --user disable myapp.service
# 查看
systemctl --user list-units
systemctl --user list-unit-files
systemctl --user list-timers
systemctl --user show myapp.service
# 日志
journalctl --user -u myapp.service
journalctl --user -fu myapp.service
# 环境
systemctl --user show-environment
systemctl --user set-environment KEY=VALUE
systemctl --user unset-environment KEY
# Linger
loginctl enable-linger
loginctl disable-linger
loginctl show-user $USER -p Linger
# 重新加载
systemctl --user daemon-reload
systemctl --user daemon-reexec