launchd通过plist中keepalive、throttleinterval等键值组合实现自动重启,仅响应进程退出而非健康状态;需额外健康检查弥补“假死”盲区,并区分系统级与用户级守护配置路径及权限。
macos 系统服务的自动重启与守护行为,核心依赖 launchd 这一统一服务管理器,而非 linux 常见的 systemd。它不靠轮询或外部监控工具实现“自愈”,而是通过 plist 配置文件声明式定义服务生命周期策略——关键在于正确设置触发条件、存活逻辑与退出响应机制。
launchd 的自动重启机制怎么起作用
launchd 本身不主动“检测”服务是否健康,而是根据进程退出状态和配置规则决定是否拉起新实例。能否自动重启,取决于 plist 中几个关键键值的组合:
- KeepAlive true:只要进程退出(无论正常还是异常),立即重启,等效于 systemd 的 Restart=always
- KeepAlive { "Crashed" = true; }:仅当进程因信号终止(如 SIGSEGV)或非零退出码崩溃时才重启,更安全
- ThrottleInterval 60:限制 60 秒内最多重启一次,防止进程反复崩溃导致系统负载飙升
- StartInterval 30:每 30 秒强制启动一次(适合无守护模式的脚本),需配合 RunAtLoad false 使用
为什么光靠 launchd 不够用
launchd 只管进程是否存在,无法识别“假死”——比如服务进程仍在,但 HTTP 接口无响应、端口不监听、或内部状态卡住。这种情况下,进程没退出,launchd 就不会干预。
补足这一短板需额外设计:
- 在服务启动脚本中嵌入轻量级健康检查(例如 curl -f http://localhost:8080/health 或 timeout 5 lsof -i :8080 >/dev/null)
- 另写一个独立的监控脚本,用 launchctl 定时触发(StartCalendarInterval 每 2 分钟执行一次),失败则执行 launchctl kickstart -k gui/$(id -u)/your.service
- 避免直接 kill -9;优先发 SIGTERM 并等待优雅退出,再由 launchd 根据配置判断是否重启
系统级 vs 用户级守护进程的关键区别
二者加载时机、权限范围和配置路径完全不同,选错会导致服务无法启动或权限不足:
- 系统级(LaunchDaemons):存于 /Library/LaunchDaemons/,以 root 身份运行,开机早期即加载,不依赖用户登录,适用于全局服务(如网络代理、日志收集器)
- 用户级(LaunchAgents):存于 ~/Library/LaunchAgents/,随当前用户登录启动,权限受限于该用户,适合个人工具类服务(如剪贴板同步、快捷键触发器)
- 系统级 plist 必须 chown root:wheel 且 chmod 644;用户级无需 sudo,但需确保脚本有可执行权限(chmod +x)
怎么验证和排查重启失效问题
服务没重启或频繁崩溃,不能只看进程是否存在,要交叉查三类信息:
- 查当前状态:
sudo launchctl list | grep your.label,观察 PID 和最后一次退出码(LastExitStatus) - 捕获错误输出:在 plist 中配置
<key>StandardErrorPath</key><string>/var/log/your-service.err</string>,查看具体报错 - 查 Unified Logging:
log show --predicate 'subsystem == "your.service.identifier"' --last 1h或实时流式查看:log stream --predicate 'subsystem == "your.service.identifier"'











