systemd 教程 / Path 监控
Path 监控
概述
Path 单元是 systemd 提供的一种文件系统监控机制,可以监控文件或目录的变化,并自动触发关联的服务。它基于 Linux 内核的 inotify 机制实现,适用于文件上传触发处理、目录监控自动部署等场景。
Path 单元文件
基本结构
Path 单元文件以 .path 为后缀,核心配置在 [Path] 段中:
# /etc/systemd/system/watcher.path
[Unit]
Description=Watch Upload Directory
[Path]
DirectoryNotEmpty=/data/uploads
Unit=processor.service
[Install]
WantedBy=multi-user.target
[Path] 段关键参数
| 参数 | 说明 | 示例 |
|---|---|---|
PathExists | 文件存在时触发 | PathExists=/tmp/trigger |
PathExistsGlob | 匹配 glob 模式的文件存在时触发 | PathExistsGlob=/data/*.csv |
PathChanged | 文件内容关闭后变化时触发 | PathChanged=/etc/config.yaml |
PathModified | 文件内容实时修改时触发 | PathModified=/var/log/app.log |
DirectoryNotEmpty | 目录非空时触发 | DirectoryNotEmpty=/data/uploads |
Unit | 触发的服务单元 | Unit=processor.service |
MakeDirectory | 自动创建监控目录 | MakeDirectory=true |
DirectoryMode | 自动创建目录的权限 | DirectoryMode=0755 |
监控类型详解
PathExists
当指定路径存在时立即触发。适用于一次性触发场景:
[Path]
PathExists=/run/myapp/trigger
Unit=myapp.service
⚠️ 注意:PathExists 只在文件从不存在变为存在时触发,如果文件一直存在则不会重复触发。
PathExistsGlob
使用 glob 模式匹配文件:
[Path]
PathExistsGlob=/data/import/*.csv
Unit=import-csv.service
支持的 glob 通配符:
| 通配符 | 说明 | 示例 |
|---|---|---|
* | 匹配任意字符(不含 /) | *.csv |
? | 匹配单个字符 | data?.txt |
[abc] | 匹配方括号内的字符 | [abc].log |
PathChanged
当文件被写入后关闭时触发。适用于监控配置文件变化:
[Path]
PathChanged=/etc/myapp/config.yaml
Unit=myapp-reload.service
💡 提示:PathChanged 在文件写入完成后才触发,适合监控需要原子更新的配置文件。
PathModified
文件内容被修改时实时触发(不需要关闭文件):
[Path]
PathModified=/var/log/app/current.log
Unit=log-processor.service
⚠️ 注意:PathModified 可能会频繁触发(每次写入都触发),需要注意服务的去重处理。
DirectoryNotEmpty
当目录从空变为非空时触发:
[Path]
DirectoryNotEmpty=/var/spool/mail
Unit=mail-notifier.service
事件触发机制
inotify 后端
systemd 使用 Linux 内核的 inotify 子系统实现文件监控:
inotify 事件类型:
├── IN_CREATE → PathExists / PathExistsGlob
├── IN_DELETE → PathExists / PathExistsGlob
├── IN_CLOSE_WRITE → PathChanged
├── IN_MODIFY → PathModified
└── IN_MOVED_TO → PathExistsGlob
查看 inotify 限制
# 查看系统 inotify 限制
cat /proc/sys/fs/inotify/max_user_instances
cat /proc/sys/fs/inotify/max_user_watches
cat /proc/sys/fs/inotify/max_queued_events
# 增加 inotify 监控数量限制
echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches
⚠️ 注意:如果监控的目录包含大量文件,可能需要增加 max_user_watches 限制。
事件去抖动
systemd 的 Path 单元内建了去抖动(debounce)机制:
PathChanged:在文件关闭后等待一小段时间再触发,避免重复PathModified:使用合理的触发间隔
如果需要更精细的去抖动控制,可以在被触发的服务中实现:
# /etc/systemd/system/process-uploads.service
[Unit]
Description=Process Uploads
[Service]
Type=oneshot
# 等待几秒确保所有文件写入完成
ExecStartPre=/bin/sleep 2
ExecStart=/opt/scripts/process-uploads.sh
Path 与 Service 联动
基本联动
Path 单元触发服务启动:
# /etc/systemd/system/watcher.path
[Unit]
Description=Watch for new files
[Path]
DirectoryNotEmpty=/data/uploads
Unit=processor.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/processor.service
[Unit]
Description=Process uploaded files
[Service]
Type=oneshot
ExecStart=/opt/scripts/process.sh
使用模板单元
可以使用模板单元,Path 单元的名称会传递给服务:
# /etc/systemd/system/[email protected]
[Unit]
Description=Watch %i
[Path]
DirectoryNotEmpty=/data/%i
Unit=processor@%i.service
# /etc/systemd/system/[email protected]
[Unit]
Description=Process %i
[Service]
Type=oneshot
ExecStart=/opt/scripts/process.sh %i
# 启动监控
sudo systemctl enable --now [email protected]
实际案例
案例 1:监控目录自动处理文件上传
场景:用户通过 FTP/SFTP 上传文件到 /data/uploads,自动触发处理脚本。
# /etc/systemd/system/upload-watcher.path
[Unit]
Description=Watch upload directory for new files
[Path]
DirectoryNotEmpty=/data/uploads
MakeDirectory=true
DirectoryMode=0755
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/upload-watcher.service
[Unit]
Description=Process uploaded files
[Service]
Type=oneshot
User=processor
Group=processor
WorkingDirectory=/data/uploads
ExecStartPre=/bin/sleep 2
ExecStart=/opt/scripts/process-uploads.sh
StandardOutput=journal
StandardError=journal
# 创建监控用户
sudo useradd -r -s /bin/false processor
# 创建目录并设置权限
sudo mkdir -p /data/uploads
sudo chown processor:processor /data/uploads
# 启用并启动
sudo systemctl enable --now upload-watcher.path
# 检查状态
systemctl status upload-watcher.path
案例 2:文件上传触发编译
场景:监控源代码目录,文件变化时自动触发构建。
# /etc/systemd/system/auto-build.path
[Unit]
Description=Watch source directory for changes
[Path]
PathChanged=/opt/project/src
Unit=auto-build.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/auto-build.service
[Unit]
Description=Auto build project
[Service]
Type=oneshot
WorkingDirectory=/opt/project
ExecStart=/usr/bin/make build
StandardOutput=journal
StandardError=journal
案例 3:监控配置文件自动重载
# /etc/systemd/system/config-watcher.path
[Unit]
Description=Watch configuration file
[Path]
PathChanged=/etc/myapp/config.yaml
Unit=config-reload.service
# /etc/systemd/system/config-reload.service
[Unit]
Description=Reload myapp configuration
[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl reload myapp.service
案例 4:邮件通知触发
# /etc/systemd/system/mail-watcher.path
[Unit]
Description=Watch for new mail
[Path]
DirectoryNotEmpty=/var/spool/mail
Unit=mail-notifier.service
# /etc/systemd/system/mail-notifier.service
[Unit]
Description=Send mail notification
[Service]
Type=oneshot
ExecStart=/opt/scripts/notify-mail.sh
User=mail
Path vs inotifywait 对比
| 对比项 | systemd Path | inotifywait |
|---|---|---|
| 集成度 | 原生 systemd 集成 | 独立工具 |
| 配置方式 | 单元文件 | 命令行参数 |
| 去抖动 | 内建支持 | 需手动实现 |
| 服务联动 | 直接触发服务 | 需要脚本包装 |
| 监控粒度 | 5 种类型 | 更多事件类型 |
| 资源占用 | 由 systemd 统一管理 | 独立进程 |
| 持久化 | systemd 管理开机启动 | 需要额外配置 |
inotifywait 替代方案
如果 Path 单元无法满足需求,可以使用 inotifywait 配合 systemd 服务:
# /etc/systemd/system/inotify-watcher.service
[Unit]
Description=Inotify watcher for /data
[Service]
Type=simple
ExecStart=/usr/bin/inotifywait -m -r -e create,modify /data/uploads
Restart=always
RestartSec=5s
💡 提示:对于简单的目录监控,优先使用 Path 单元;对于需要复杂事件过滤的场景,考虑使用 inotifywait 或 fswatch。
调试 Path 单元
检查 Path 状态
# 查看 path 单元状态
systemctl status watcher.path
# 查看 path 单元详细信息
systemctl show watcher.path
# 查看触发的服务状态
systemctl status watcher.service
手动测试触发
# 触发 PathExists
touch /tmp/trigger
# 触发 DirectoryNotEmpty
echo "test" > /data/uploads/test.txt
# 触发 PathChanged
echo "new_value" >> /etc/myapp/config.yaml
查看日志
# 查看 path 单元日志
journalctl -u watcher.path
# 查看触发的服务日志
journalctl -u watcher.service
# 实时监控
journalctl -fu watcher.path
常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Path 不触发 | Path 单元未启动 | systemctl start watcher.path |
| 触发后服务未启动 | Unit 配置错误 | 检查单元文件名拼写 |
| inotify 资源不足 | 监控数量超限 | 增加 max_user_watches |
| 频繁触发 | PathModified 过于敏感 | 改用 PathChanged 或增加去抖动 |
| 权限问题 | systemd 无权访问目录 | 检查目录权限和 SELinux/AppArmor |
⚠️ 注意:如果使用 SELinux,可能需要为监控的目录设置正确的上下文标签。
实用命令汇总
# 启用并启动 path 单元
sudo systemctl enable --now watcher.path
# 停止并禁用
sudo systemctl disable --now watcher.path
# 重新加载配置
sudo systemctl daemon-reload
# 查看所有活跃的 path 单元
systemctl list-units --type=path
# 查看 path 单元依赖
systemctl list-dependencies watcher.path