关于 macos-load-doctor
macOS Load Doctor 是用于诊断 Mac 变慢或发热原因的技能,通过系统与进程测量提供证据。它侧重追踪资源使用的归属,并遵守共享机器的授权边界,而非以清理进程为目标。
主要功能
- 01系统负载与进程盘点
- 02追踪进程父链及归属
- 03分类泄漏、风暴与级联负载
- 04检查内存压力与活动
- 05遵循授权边界的处理指引
适用场景
- 01调查多个应用同时超时
- 02追踪持续增长的子进程
- 03区分忙循环与进程风暴
- 04排查系统变慢时的内存压力
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ daymade/ claude-code-skills --skill macos-load-doctor安装后在 Claude Code 中输入/macos-load-doctor确认已加载。
附带文件references/incident-playbook.mdscripts/load_census.sh
详细介绍
macOS 负载诊断
找出导致 Mac 变慢或发热的原因,追溯责任归属,并在共享机器的边界内采取行动。本技能的目标是给出准确、有证据支持的归因,而不是清理进程。
首要规则
在证实并非环境问题之前,应将影响整个设备群的症状视为环境问题。 当多个相互独立的应用、钩子或 Agent 同时开始超时或运行缓慢时,它们都不是嫌疑对象——一定有某种因素在让它们集体资源不足。先检查机器;只有机器状况正常后,才调试个别对象。
分诊(按顺序读取并执行)
1. 始终先检查负载
sysctl -n vm.loadavg; uptime
预期结果:三个数字,例如 { 3.12 4.01 5.20 }。根据核心数来判断,而不是看绝对值:在现代 Mac 上,个位数表示负载平稳,几十表示繁忙;数百意味着运行队列的任务数是可用核心数的数倍,机器上的所有进程都会受影响——钩子会超时、按键会延迟、推送会停滞。如果负载达到数百,但 CPU 列中没有明显的罪魁祸首,通常意味着有数百个小进程,而不是一个大进程。
2. 进程普查——运行随附脚本
LOAD_DOCTOR_DIR='<absolute directory containing this SKILL.md>'
bash "$LOAD_DOCTOR_DIR/scripts/load_census.sh"
从已加载的 Skill 条目中解析该目录;调用方的项目目录不是 Skill 目录。普查结果中的各项读数回答不同的问题:
- 按父进程聚合(PPID 聚合) — 谁有大量子进程? 数量较大说明存在扇出和资源开销,但不能证明有泄漏。在有限时间段内,结合所有者当前的工作负载和预期生命周期,对比进程的创建、退出和数量。稳定的进程池或活跃会话的辅助进程可能完全正常;若要认定发生泄漏,需要有证据表明子进程的存续时间超过了其服务对象的工作时长,或相同工作完成后保留的进程数量仍在增长。不要通过终止会话来人为制造证据;应使用已有的生命周期记录或自行测试。
- 按累计 CPU 时间 — 谁已经连续消耗 CPU 数小时? GUI 应用或辅助进程若以约 100% 的 CPU 运行数天,说明存在忙循环,而不是短暂峰值。
- 按瞬时 %CPU — 谁现在正在消耗 CPU? 可捕捉到累计视图会弱化的当前风暴。
对于保留的 Agent/MCP 子进程,在将其视为无用进程前,请阅读有状态 Agent 和 MCP 子进程。
3. 追溯责任归属
沿着嫌疑进程的父进程链向上追踪,直到找到明确的责任方:
ps -o pid,ppid,etime,command -p <pid> # then repeat on its PPID
launchctl list | awk '$2 != "-" && $2 != "0"' # jobs with abnormal last-exit codes
launchctl 筛选结果也会打印标题行,而且许多系统守护进程会一直处于 -9(SIGKILL)状态——这是常见的背景噪声,不代表崩溃循环。判断是否为重启循环,要看多次运行之间任务的 PID 是否一直变化,而不是看某个单独的状态值。
归因可以确定是哪个产品或会话拥有该进程、它是否应当长期运行(守护进程),还是应当短期运行(忘记退出的辅助进程),以及谁有权停止它。
4. 提出修复方案前,先判断问题类型
| 类型 | 特征 | 常见原因 |
|---|---|---|
| 疑似泄漏 | 可比工作完成后,保留的子进程数量仍在增长;或辅助进程的存续时间超过其文档所述的生命周期 | 缺少清理;认定为泄漏前,先确认工作负载和生命周期 |
| 扇出/进程池 | 大量子进程与活跃会话相关,或对应一个配置稳定的进程池 | 按线程分配进程可能开销很大,但不一定是泄漏 |
| 风暴 | 大量进程的运行时间只有几秒,且派生速率很高 | 未限速的循环(测试回放、批量扫描、未采用退避机制的重试) |
| 忙循环 | 单个进程以约 100% 的 CPU 运行,累计时间已达数天 | 应用持续轮询却没有休眠 |
| 级联 | 负载很高,但嫌疑进程分散 | 某个系统服务放大了每个新进程的影响(安全扫描、文件同步、Spotlight) |
每种情况的修复方法都不一样:确认发生泄漏时,应修复或重启进程生成器;发生风暴时,应在循环处限速;出现忙循环时,应重新启动应用或禁用其扫描;出现级联时,应减少新进程,而不是让它们运行得更快。
5. 在边界内行动
在共享机器上,诊断只读;修复由负责人负责。
- 绝不终止其他会话、Agent 或用户的进程。所谓“重启服务”,如果副作用是杀死守护进程的子进程,那只是用更好听的说法包装了同一操作——仍然算作终止进程。
- 你可以自行执行的操作:限制自己的循环频率、调整自己进程的优先级、停止自己的后台任务。
- 终止或重启其他会话、Agent、服务或用户的进程,需要获得当前用户的明确授权。可以与同事协调责任归属,但他们的同意并不构成该授权。只暂停需要审批的干预;继续进行不依赖该审批的已授权只读调查。
- 对于命名告警,在判定原因前,先还原其记录的时间窗口、计量单位和触发计算方式。另一个进程负载更高,并不能解释该告警为何触发。应分别说明告警是否有效,以及机器的实际负载情况。
- 对于诊断或修复请求,进度报告不代表任务结束。如果仍有已授权的探查可以区分相互竞争的解释,就不要在原因尚未查明时发送结案答复。只有找到能解释所请求症状或告警的具体机制,并取得能够区分这些解释的证据,诊断才算完成;获批的修复还须通过独立回读来确认。单独要求快照的请求可在提供快照后结束;在诊断进行中询问状态时,应提供进度更新并继续调查。明确要求暂停时,应遵从。如果外部条件阻止了所有剩余探查,应将其列为阻碍,并说明尚缺的证据,不要声称任务已完成。
- 当前用户授权干预后,应执行并验证;将已获授权的操作作为另一份报告交还,不算完成。
- 任何修复后(无论由你还是负责人执行),都要回读:重新运行
sysctl -n vm.loadavg和进程清点,然后通过实际使用该功能的使用方来验证用户原本需要的能力。禁用服务可能会减轻负载,却同时移除了该能力:在功能恢复之前,应报告为缓解措施。重新加载配置前,应确认其影响范围是否涵盖其他线程或服务。比较目标与非目标辅助进程在操作前后的情况;主进程 PID 未变,并不能证明其他辅助进程未受影响。
内存证据
对于内存问题,应在统计进程数的同时测量内存压力和活动情况:
sysctl vm.swapusage
vm_stat -c 4 2
ps -axo pid,ppid,rss,command
读取 vm_stat 打印的页面大小;其第一行是累计值,后续行显示时间间隔内的活动情况。现有交换空间分配量并不能证明当前正在发生抖动:应检查采样时间窗口内的换入/换出活动,并报告该时间窗口。RSS 总量可能会重复计算共享的常驻页面,因此不代表唯一的物理内存。对于可疑进程,使用 vmmap -summary <pid> 区分其报告的物理占用量与 RSS;单独保留报告的 GPU/IOKit 分配量,不要悄悄将不同口径的计数相加。仅凭大量分配或压缩内存快照,无法确定哪些对象被保留,也不能据此认定存在内存泄漏。
Apple 的内存指南将内存压力定义为根据可用内存、交换速率、固定内存和缓存文件进行评估。执行诊断的 Agent 会结合这些信号与观察到的工作负载进行判断;进程清点不会自动判定是否存在内存泄漏。
常见泄漏模式
包含实际探测输出的案例见 references/incident-playbook.md——当清查发现你从未见过的情况时,或需要为即将撰写的报告寻找先例时,请阅读该文档。
- 每个线程启动 MCP 进程:Agent 运行时会为每个对话线程启动完整配置的 MCP 集合,且从不回收这些进程;几天内,数百个代理/辅助进程便会累积在同一个守护进程下。带 GUI 的 MCP 服务还会给 WindowServer 造成巨大负载。
- 未限速的批处理循环:没有速率限制的重放/模糊测试/迁移循环,对机器上的其他部分来说与 fork 炸弹无异——速率相同、发热相同,还会导致安全扫描负载级联增加。
- GUI 忙循环:菜单栏应用或设置应用在轮询时没有休眠——单个核心连续数天保持 100% 占用,直到清查累计时间时才会被发现。
- 反复重启循环:launchd 任务每隔几秒就崩溃并重启——
launchctl list会显示非零的上次退出代码,以及不断变化的 PID。
故障排除
- 负载很高,但每次读数看起来都正常:可疑进程的生命周期很短——直接测量 fork 速率:间隔几秒获取两次
ps -Ao pid=快照,统计只出现在第二次快照中的 PID 数量(comm -13 <(sort old.txt) <(sort new.txt) | wc -l,再除以时间间隔)。每秒几十个说明发生了风暴;只有几个则属于正常波动。 - 显而易见的高 CPU 进程其实无辜:WindowServer、终端或屏幕共享客户端的 CPU 占用率很高,往往是实际原因的下游表现(数百个 GUI 应用副本都需要窗口服务)。继续追查。
- 所有线索都指向系统服务:这是级联现象——解决方法是降低到达该服务的新任务速率,而不是处理服务本身。