[BUG] macOS 服务模式:GUI 会话被强杀/失联后 root 版 verge-mihomo 残留数日占用 mixed-port,新实例 bind: address already in use
问题描述 / Describe the bug
macOS 上启用服务模式后,verge-mihomo 内核由特权服务 clash-verge-service(LaunchDaemon,KeepAlive=true)以 root 拉起。当持有该内核的 GUI 会话非正常结束(被强杀、内存压力下被系统终止、崩溃等,未走正常退出流程)后,root 内核不会随之停止,会持续占用 mixed-port(7890)与 DNS 端口(1053),成为"幽灵内核"。
之后重新启动 GUI 时,因 owner 会话(随机 token + generation)与残留内核不匹配,GUI 无法接管服务内核,转而以 Sidecar(普通用户)模式另起内核 → 新内核 bind: address already in use,代理不可用。只能手动 pkill -U root -x verge-mihomo 或重装服务恢复。与 #6741 同族:服务模式内核生命周期绑定于易失的 GUI 会话,会话断后无回收机制。
复现步骤 / To Reproduce
安装/启用服务模式(特权助手拉起 root
verge-mihomo)让 GUI 非正常结束,例如任选其一:
- macOS 内存压力下被系统终止(本机实际发生过 Jetsam 事件)
- 活动监视器强制退出 /
kill -9 - 崩溃
(注意:正常退出会走
feat::quit()→ IPCstop_core(),不会触发本 bug)
实测触发时刻:8/29 23:41 系统重启(近期首次)后,GUI 由 LaunchAgent 于 23:42:24 自动启动;8/30 00:05:43 该实例被非正常终止(runningboardd 记录
com.apple.frontboard.after-life.interrupted,无崩溃报告,符合手动/系统强杀特征),44 秒后服务模式内核被拉起并成为幽灵。观察:root 版
verge-mihomo继续运行,持续监听 7890/1053(属主 root,普通用户无法 kill)重新打开 Clash Verge → GUI 以 Sidecar 模式启动新内核 → 报错:
level=error msg="Start Mixed(http+socks) server error: listen tcp :7890: bind: address already in use" level=error msg="Start DNS server(TCP) error: listen tcp 0.0.0.0:1053: bind: address already in use"代理失效,直到手动清理或 GUI 卸载/修复服务
实际观测 / Observed
- 近期首次系统重启:8/29 23:41:48(
kern.boottime,之后未再重启) - GUI 实例 A(pid 455)于 8/29 23:42:24 由开机自启 LaunchAgent 拉起;8/30 00:05:43 被非正常终止(runningboardd:
after-life.interrupted,即进程非正常退出后的善后断言;DiagnosticReports无对应 .ips 崩溃报告——符合手动强杀或系统终止特征,而非常规 crash) - 44 秒后(8/30 00:06:27):特权服务与 root 版
verge-mihomo被拉起,helper bundle 同时落盘(服务安装/启动时刻);此后该 root 内核残留 5 天(8/30 → 9/4 手动清理),期间 launchd 无任何启停事件、系统未重启、内核 PID/启动时间全程不变 - 本机 9/4 00:35 确有 Jetsam(内存压力)事件,有进程按
per-process-limit被终止;快照中 GUI 本身内存占用很小,未直接抓到其被 jetsam 终止的记录——内存压力致 GUI 被系统强杀属于候选触发路径(也可能为手动强杀) - 结论:GUI 被强杀(8/30 00:05)后未走正常退出流程,
stop_core未送达服务端;随后被拉起并遗留在服务端的 root 内核失去 owner,成为无法回收的幽灵
根因分析 / Root Cause
疑似位于服务端 clash-verge-service-ipc:
start_core()停旧内核时,ChildGuard::drop()内tokio::spawn(child.kill().await)异步 kill,不等待进程退出/端口释放(当前 main 仍如此,见 #6741)- GUI 侧 owner 会话失联后,
recover_after_owner_loss只让 GUI 放弃服务模式(mark_service_unavailable_after_owner_loss),无法回收服务端那只失联的 root 内核 - 客户端与特权服务之间缺少"孤儿 root 内核检测与回收"机制
期望行为 / Expected Behavior
- GUI 会话结束/失联后,服务端应在超时后主动回收其拉起的 root 内核;或客户端启动时先检测并清理孤儿 root 内核,再决定运行模式
- 服务端
start_core应在 kill 旧进程后等待其退出并确认端口释放(或带重试)再启动新内核
日志 / Log
[2026-09-03 21:36:53.656] level=error msg="Start Mixed(http+socks) server error: listen tcp :7890: bind: address already in use"
[2026-09-03 21:36:53.656] level=error msg="Start DNS server(TCP) error: listen tcp 0.0.0.0:1053: bind: address already in use"
[2026-09-03 21:36:53.657] level=error msg="Start DNS server(UDP) error: listen udp 0.0.0.0:1053: bind: address already in use"
操作系统 / OS
- Windows
- macOS
- Linux
Source: clash-verge-rev/clash-verge-rev