systemctl list-dependencies 仅显示单元文件中显式声明的 wants=、requires=、after=、before= 关系,不包含运行时动态依赖;需用 --all、--reverse、ldd、strace、journalctl 和 systemd-analyze 等工具分层排查四类依赖。

systemctl list-dependencies 能看到哪些依赖
它只显示 systemd 单元(service、target、socket 等)在单元文件中显式声明的 Wants=、Requires=、After=、Before= 关系,不反映运行时动态加载的模块、共享库或配置文件依赖。
实操建议:
- 加
--all参数展开全部层级(默认只显示一级):systemctl list-dependencies --all sshd.service - 用
--reverse查谁依赖某个服务(比如查谁拉起了docker.service):systemctl list-dependencies --reverse --all docker.service - 注意
WantedBy=multi-user.target这类“反向依赖”不会出现在正向list-dependencies输出里,得靠--reverse
ldd 和 strace 能补上哪些 systemd 看不见的依赖
systemd 不管二进制本身依赖什么库,也不管启动后打开哪些文件或 socket。这时候 ldd 和 strace 是真实世界里的“照妖镜”。
常见错误现象:服务明明 systemctl start 成功,但日志里报 No such file or directory 或 cannot open shared object file —— 很可能缺的是 .so 或配置路径。
实操建议:
- 先定位主程序路径:
systemctl show -p ExecStart sshd.service | grep ExecStart,再对二进制跑ldd /usr/sbin/sshd - 快速抓启动时的文件访问:
strace -e trace=openat,openat2,stat -f systemctl start myapp.service 2>&1 | grep -E "(No such|Permission denied|failed)" -
strace输出里出现/etc/myapp/config.yaml: No such file,说明 systemd 启动成功只是“进程起来了”,但业务逻辑卡在读配置
journalctl -u + 配置文件路径是验证依赖链的关键证据
很多服务依赖不是写死在单元文件里的,而是由环境变量、外部配置或上游服务状态间接决定的。这时候日志和配置才是最终判决依据。
使用场景:服务启动失败,systemctl status 只显示 failed,但没说为什么;或者它启动了,但功能异常。
实操建议:
- 查完整启动过程:
journalctl -u nginx.service --since "1 hour ago" -n 100,重点看execv行和第一条 ERROR - 确认实际加载的配置:
nginx -t或httpd -t,它们会打印出真正被读取的.conf路径,而不是你以为的/etc/nginx/nginx.conf - 如果服务依赖另一个服务提供的 socket(如
php-fpm.sock),检查该 socket 是否存在且权限正确:ls -l /run/php/php-fpm.sock,getenforce结果为Enforcing时 SELinux 可能拦截访问
systemd-analyze verify 和 dot 导出适合排查循环依赖和拼写错误
当 systemctl daemon-reload 没报错,但服务启不动,或者 list-dependencies 输出里出现可疑的重复项,大概率是单元文件里写错了名字,或者 After= 和 Before= 形成闭环。
性能影响:循环依赖会导致 systemd 卡住,超时后报 Job for xxx.service cancelled,而不是明确的错误提示。
实操建议:
- 检查语法和命名:
systemd-analyze verify /etc/systemd/system/myapp.service,它会指出Wants=nonexistent.service这类拼写错误 - 导出依赖图看结构:
systemd-analyze dot 'myapp.service' | dot -Tpng > deps.png,用图像工具打开,一眼识别双向箭头或长链 - 注意
dot默认不包含 target 单元,加--to-pattern="*.target"才能看到完整启动目标路径
真正的难点不在命令怎么敲,而在于分清“声明的依赖”“链接的依赖”“运行时的依赖”和“配置触发的依赖”——这四层叠在一起,漏掉任何一层,排查就会绕远路。










