一次 Codex Desktop 把本机 CPU 点燃的排障:Crashpad 孤儿、GPU 进程与 syspolicyd
2026-06-08 晚上,本机突然出现明显卡顿,活动监视器里最刺眼的几项是:
syspolicyd超过 150%- 两个
browser_crashpad_handler各自超过 100% Codex (Service)超过 100%
这类现场最容易误判成“系统自己抽风”或“某个前台应用太重”。这次比较有意思的地方在于,三个热源其实分属两层故障:一层是安装器卷里跑出来的 Codex Crashpad 孤儿进程,另一层是当前 Codex Desktop 的 GPU 辅助进程稳定 100% 复发,并把 macOS 安全策略服务一起拖进高负载。
第一层:从安装器卷跑出来的 Crashpad 孤儿
进程列表里最热的 browser_crashpad_handler 并不是当前 /Applications/Codex.app 的子进程,而是来自:
/Volumes/Codex Installer/Codex.app/.../browser_crashpad_handler
它们的父进程已经变成 launchd,而 hdiutil info 和挂载表里已经看不到对应的 Codex Installer 卷。也就是说:安装器卷里的 Codex 曾经启动过,卷后来消失了,但 Crashpad 自监控进程留在系统里继续空转。
普通 TERM 没有把它们带走,最后用强制结束才止住这一层。强杀后,来自 /Volumes/Codex Installer 的高 CPU Crashpad 没再回潮。
经验:看到 browser_crashpad_handler 高 CPU 时,不要只看进程名。路径比名字重要。尤其要区分:
/Applications/Codex.app/...:当前安装版/Volumes/Codex Installer/Codex.app/...:安装器卷或临时卷里的副本
后者如果父进程丢失,就优先按孤儿进程处理。
第二层:Codex 的 GPU 辅助进程稳定复发
第一层处理后,当前 Codex 里又出现了新的热点:
Codex (Service) --type=gpu-process
它稳定跑到 90%-100% CPU。杀掉这个 GPU 进程后,Codex 主进程会马上重建一个新的 GPU 进程,新进程继续 100%。这说明它不是单个子进程僵死,而是当前运行态的 GPU 路径会稳定触发。
临时止血方法是暂停这个 GPU 进程,而不是继续杀:
kill -STOP <gpu-process-pid>
暂停后,该进程 CPU 立即降到 0,Codex 主进程和后台对话仍然存活。代价是窗口渲染可能变慢或局部冻结,但比直接退出整个 Codex 更适合现场抢救。
同时,本地 Chromium 状态里可以看到硬件加速曾经是开启路径:
hardware_acceleration_mode_previous: true
因此做了持久规避:把本地状态改为下次启动禁用硬件加速,并把 GPU / Shader / Dawn 缓存目录移到带时间戳的备份目录。这个改动不修改应用包,不破坏签名,下一次重启 Codex 后生效。
第三层:syspolicyd 为什么也被拖高
排查中还有一个非常有辨识度的信号:spctl 对 /Applications/Codex.app 做执行评估时报:
/Applications/Codex.app: Too many open files
系统日志里同时出现:
UNIX error exception: 24
Failed to generate SecStaticCode ... error: 100024
Security policy would not allow process: ..., /Applications/Codex.app/Contents/MacOS/Codex
这里的关键是 error 24,也就是文件描述符相关错误。它让 syspolicyd 进入高负载的安全评估状态。由于这台机器开启 SIP,尝试重启 com.apple.security.syspolicy 会被系统拒绝:
Operation not permitted while System Integrity Protection is engaged
所以最终策略不是硬重启系统服务,而是停止继续喂它更多高频评估请求,先把 Codex GPU 热点压住,再等待它自然回落。必要时,完整的收尾动作应该是退出并重新打开 Codex,让“禁用硬件加速 + 清空 GPU 缓存”的改动生效。
最小处置顺序
这次比较可靠的处置顺序是:
- 先按 CPU 排序确认真实热点,不凭活动监视器截图猜。
- 对
/Volumes/Codex Installer里的 Crashpad 孤儿进程强制结束。 - 对当前 Codex 的
--type=gpu-process先验证:杀掉是否会马上重生并继续 100%。 - 若稳定复发,暂停 GPU 进程临时降温。
- 写入禁用硬件加速的本地状态,并备份移走 GPU / Shader / Dawn 缓存。
- 下次重启 Codex 后验证是否还会创建高 CPU GPU 进程。
有趣的地方
这次现场像三层套娃:
- 最开始看起来是
syspolicyd爆了。 - 往下看,是 Crashpad 爆了。
- 再往下看,Crashpad 里一部分来自已经消失的安装器卷,另一部分只是正常挂着。
- 真正会复发的核心,则是当前 Codex Desktop 的 GPU 子进程。
最容易踩坑的是把所有 browser_crashpad_handler 都当成一类,或者把 syspolicyd 当根因。实际上 syspolicyd 更像被拖下水的裁判:它在不断判定一堆失败的执行/签名/安全评估请求,最后自己也开始发热。
后续建议
短期建议:
- 避免从
.dmg或安装器卷里直接长期运行 Codex。 - 如果 Codex 窗口再次卡顿,优先看
Codex (Service) --type=gpu-process。 - 如果 GPU 进程再次 100%,先暂停或退出重开 Codex,不要反复杀,因为它会自动重生。
产品侧建议:
- Codex Desktop 可以提供一个用户可见的“禁用硬件加速 / 安全渲染模式”开关。
- GPU 进程连续重启或持续 100% 时,主进程应该自动退到软件渲染。
- Crashpad 自监控进程应更积极地处理父进程丢失和应用卷消失的场景。
这次的教训很朴素:本机 CPU 爆炸时,不要只问“哪个名字最高”,要问“这个进程从哪里来、父进程是谁、杀掉后会不会重生、路径是不是当前安装版”。路径、父子关系、复发性,比单点 CPU 数字更接近真相。