☰
崩溃也不怕:Insomnia 合盖防休眠恢复机制揭秘——recovery journal 与 launchd backstop 双保险设计
2026/10/15 18:03:47 网站建设 项目流程

【免费下载链接】Insomnia

A macOS menu bar app that safely keeps your MacBook awake with the lid closed, for timed agent sessions.

项目地址:https://gitcode.com/gh_mirrors/insomnia6/Insomnia
点击查看免费下载

Insomnia 是一款 macOS 菜单栏应用,专为 AI 编程代理(agent)的长时任务设计,能让 MacBook 在合盖状态下安全地保持唤醒而不休眠。但"让电脑不休眠"这件事本身就动了系统的电源策略,一旦应用中途崩溃,机器可能一直卡在"禁止睡眠"的状态。Insomnia 为此设计了 recovery journal(恢复日志)与 launchd backstop(兜底代理)的双保险恢复机制,本文带你完整看懂它的工作原理。

为什么防休眠应用最需要"恢复机制"

Insomnia 工作时会对机器做一系列系统级改动:

  • 通过sudo pmset关闭睡眠(disablesleep 1)
  • 必要时开启低电量模式(Low Power Mode)
  • 合盖时调暗屏幕与键盘背光、静音音频
  • 暂停(freeze)冻结列表中的应用进程,降低功耗

这些改动在正常情况下会随会话结束自动撤销。但问题在于:应用崩溃、被强杀、甚至整台机器重启时,谁负责撤销?如果没人管,MacBook 会一直禁止睡眠、一直耗电。

Insomnia 的答案是:不信任内存,只信任磁盘。

第一重保险:recovery journal 恢复日志

先写日志,再动手改机器

Insomnia 的核心不变式只有一条:每一次改动,都先写进磁盘上的恢复日志,然后才真正执行。

会话信息存在session.json,改动记录存在state.json,位置在~/Library/Application Support/Insomnia/(完整路径定义见 Paths.swift)。state.json就是所谓的 recovery journal,它的结构定义在 RuntimeState.swift:

日志条目含义恢复方式
sleepDisabledByUs我关掉了系统睡眠恢复时执行sudo pmset -a disablesleep 0
lowPowerSetByUs我开启了低电量模式恢复时执行sudo pmset -b lowpowermode 0
frozenProcesses我冻结了哪些进程(含启动时间、boot session 身份指纹)校验身份后发送 SIGCONT 恢复
savedDisplayBrightness/savedKeyboardBrightness合盖前保存的亮度开盖或恢复时还原
savedAudioOutputs/savedMuted合盖前保存的音量与静音状态重新打开应用后还原
appNapOverrides我为哪些 agent 应用改过 App Nap 设置用defaults写回原值

只要日志里还有未撤销的条目(代码里叫isDirty),恢复流程就会继续尝试;每条条目必须"撤销成功"之后才从日志中清除。这一步的顺序保证了:写日志失败时,对应的改动根本不会执行。

日志坏了怎么办:宁可保留,绝不删除

恢复机制最怕两件事:误删证据、误判状态。Insomnia 的选择是——

  • 撤销失败的条目保留在日志中,下一轮继续重试;
  • 无法解析的日志文件原样保留,当作"需要人工处理",而不是当作"一切干净";
  • 无法解析的session.json会被重命名为session.json.unreadable-<时间戳>挪到一边(永不删除、永不覆盖),文件字节完整保留作为证据。

第二重保险:launchd backstop 每分钟巡场

独立于应用的 launchd 代理

就算应用进程已经消失了,磁盘上的session.json里仍然写着会话的截止时间。Insomnia 在安装时(install.sh)会注册一个独立的 launchd 用户代理com.insomnia.backstop(plist 位于~/Library/LaunchAgents/),它的行为很简单粗暴:

  • 加载后立即运行一次,之后每 60 秒再运行一次;
  • 每次运行只做一件事:读session.json和state.json,判断会话是否已到期、日志是否"脏";
  • 如果会话还有效且未到期,什么都不做,直接退出;
  • 如果会话已到期(或应用已不在)而日志还有未撤销的条目,就按日志逐条撤销,并在全部干净后原子地发布新的日志文件。

也就是说:应用的正常运行路径和崩溃路径,走的是同一份日志、同一套撤销逻辑,backstop 只是"应用没空管时"的替补。调度逻辑的 Swift 侧实现在 LaunchdBackstop.swift。

签名校验:被篡改的脚本永远跑不起来

backstop 脚本backstop.sh(源码在 scripts/backstop.sh)在安装时被封入应用包内(Insomnia.app/Contents/Resources/backstop.sh),并在签名之前完成拷贝。launchd 每次运行它之前,会先用codesign --verify --strict校验整个应用包的签名是否与 plist 中固定的代码要求(code requirement)一致——校验不过,脚本一行都不会执行,只会在~/Library/Logs/Insomnia/insomnia.log里记一条错误日志。

这意味着任何人(或任何恶意软件)在磁盘上偷偷修改 backstop.sh,都只会让 backstop 静默罢工并留下日志,而不可能借此获得每分钟一次的执行机会。

双保险如何配合:同一把锁下的事务

应用和 backstop 可能同时想动机器,怎么办?它们共用同一把文件锁.recovery.lock(内核级flock,见 RecoveryLock.swift):

  1. 每次运行都是一笔完整事务:拿锁 → 读session.json与state.json→ 决策 → 撤销 → 原子发布新日志 → 释放锁;
  2. 锁文件永不删除,双方永远锁的是同一个 inode,避免"各锁各的";
  3. 锁由内核持有在打开的文件描述符上,进程崩溃时内核自动释放,永远不会死锁;
  4. backstop 等锁 10 秒拿不到就退出码 75,一分钟后再来;
  5. 一个仍在运行的特权命令(如卡住的sudo pmset)会自己持有锁,双方都得等它结束。

还有一处精妙设计:卡住的sudo pmset只会被发 SIGTERM,永远不发 SIGKILL——因为杀掉 sudo 可能留下一个以 root 运行、脱离事务管控的 pmset,反而会在日志"翻篇"后继续改电源设置。此时宁可让恢复流程整体暂停,等命令自然退出。

崩溃后的真实恢复流程

以"应用被强杀、会话已到期"为例,完整链路是这样的:

  1. 60 秒内,launchd 启动 backstop,先codesign校验应用包签名;
  2. 拿到.recovery.lock,读session.json发现endsAt已过,读state.json发现有未撤销条目;
  3. 按日志逐条恢复:sleepDisabledByUs→sudo -n pmset -a disablesleep 0,lowPowerSetByUs→ 关掉低电量模式;
  4. 对frozenProcesses条目,backstop自己不直接发信号,而是调用已安装应用二进制的一次性模式Insomnia --resume-frozen(实现见 ResumeFrozenCommand.swift):逐条比对进程的启动时间(精确到微秒)和 boot session,确认"这个 pid 确实是我冻结的那个进程"后才发 SIGCONT;
  5. 全部撤销成功后原子发布干净的日志,退出码 0。任何一步失败则退出码 1,保留日志,下一分钟自动重试。

恢复机制的边界:哪些情况需要人工

Insomnia 的态度很诚实:恢复不是"一切总能被自动撤销"。以下情况需要你留意(详见 docs/recovery.md):

  • 🔊音频恢复需要应用本体:backstop 能保留音量/静音条目,但还原 CoreAudio 状态必须重新打开 Insomnia;
  • 🧊无法证实身份的冻结进程不会被自动恢复:宁可让它保持暂停,也不盲发信号给一个来路不明的 pid,需要你自己核实;
  • 🔋backstop 不监测电池与温度:定时结束是目标,不是安全保证;
  • ⚠️菜单栏出现恢复警告时,先处理再离开电脑。

这些真实 Mac 上的崩溃、重启、升级检查记录在 release-validation.md,设计目标与不变式则写在 spec.md 第 8 节。

相关文件索引

  • 恢复机制完整文档:docs/recovery.md
  • backstop 脚本源码:scripts/backstop.sh
  • launchd 代理调度:Sources/Insomnia/Core/LaunchdBackstop.swift
  • 共享恢复锁:Sources/Insomnia/Core/RecoveryLock.swift
  • 日志结构定义:Sources/Insomnia/Model/RuntimeState.swift
  • 冻结进程恢复模式:Sources/Insomnia/Core/ResumeFrozenCommand.swift
  • 合盖行为细节:docs/lid-close.md

总结

Insomnia 的恢复设计可以浓缩成三句话:改动前先写日志、应用和 backstop 共用一把锁、拿不准就保留证据等重试。recovery journal 让"改了什么"永远可查,launchd backstop 让"谁来改回去"永远有主,两者配合让一个会动系统电源策略的菜单栏小工具,在崩溃面前也守住了底线。

【免费下载链接】Insomnia

A macOS menu bar app that safely keeps your MacBook awake with the lid closed, for timed agent sessions.

项目地址:https://gitcode.com/gh_mirrors/insomnia6/Insomnia
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询