Skip to content

07 · Clean 清理引擎

Clean 的难点不是缓存目录多,而是如何在目录很多、大小计算昂贵、应用仍在运行的情况下保持结果准确。

主流程

参数 / whitelist
   │
   ▼
`perform_cleanup` 按 16 个 section 发现候选
   │
   ▼
`safe_clean`:保护检查 → 去重 → 并行算大小
   │
   ▼
删除前运行状态 guard → `safe_remove`
   │
   ▼
精确累计数量/大小 → section 与全局 summary

bin/clean.sh 负责参数、权限、交互与汇总;lib/clean/* 负责不同生态的目标发现。模块覆盖系统缓存、浏览器、开发工具、云与 Office、虚拟化、应用残留、设备备份、Time Machine、大文件线索和项目构建产物。

候选先规范化再计价

_safe_clean_impl 先做保护与白名单检查,再归一化路径并消除父子重复。若同时保留目录和内部文件,统计会重复、删除顺序也会制造假失败。超过 50 个候选时使用排序/awk 的批量路径,避免 Bash 循环的二次复杂度。

大小计算并行执行,最大任务数默认 15;文件很多时使用批量 stat。并发只用于只读阶段,实际删除仍通过安全汇点,避免多个写操作同时改变彼此的路径前提。

运行状态在最后一刻复检

Xcode、Simulator、Codex 等工具运行时,其缓存可能正在写入。发现阶段可以跳过它们,但从发现到删除之间进程状态会变化,所以 Clean 支持 delete guard,在调用 safe_remove 前再次判断。

这是一种实用的 TOCTOU 缓解:无法冻结系统状态,就把容易变化的断言尽量靠近副作用。

Dry run 使用同一条计划路径

Dry run 不是另一套粗略扫描。候选仍经历保护、白名单、去重与大小计算,最终写入预览 ledger 并汇总“将释放”的空间。root 场景的预览文件经过安全 staging 发布,防止特权写入被 symlink 劫持。

16 个 section 的意义

Section 既是 UI 分组也是错误隔离边界。云盘扫描可以拥有更长超时,外部卷可以选择性启用,项目产物可以要求活动度判断。统一删除内核不意味着所有发现器采用同一风险策略。

命令已移除复杂的 categories/select/exclude 参数,保留 --dry-run、外部卷和白名单等少量开关。这减少了用户以为“选了一个类别就精确知道会删什么”的错觉,实际目标仍以预览为准。

关键源码索引

下一章看风险更高的 Uninstall:它不仅删除应用本体,还要证明哪些残留真的属于同一个应用。

独立源码研究笔记 · 非 Mole 官方文档