Join Nostr
2026-10-28 14:14:41 UTC

资讯同步 on Nostr: #V2EX ### [分享创造] 做了个菜单栏小工具:找出是哪个进程把 Mac ...

#V2EX
### [分享创造] 做了个菜单栏小工具:找出是哪个进程把 Mac 烤热的,把它挪到能效核继续跑

跑多 agent 的时候遇到一个很烦的问题:Mac 烫到 70℃,风扇全速,但打开活动监视器根本看不出是谁干的。

![界面截图]( )

十几行一模一样的 `node`,CPU 百分比还在跳,一行行对下去二十分钟没了。而且很多时候元凶是某个跑了两小时的 agent ,你也不想直接 kill ——中断了上下文就丢。

所以写了个菜单栏 App ,叫 **Manatee**,就做两件事:找出谁在烫,把它挪到能效核。

**怎么找出「谁」**

难点是同一个 agent 会 fork 出一堆同名进程,按可执行名分组没有意义。所以按 `rootPid` 聚合成会话,一个会话一行,CPU 百分比是整棵进程树求和。

另一个坑是判定。编译的时候瞬时 800% 是正常的,不能算异常。所以记录每个会话最近 6 分钟的采样,**连续 5 分钟超过 200%(两个核)**才标记出来。实测这个窗口能把编译抖动和「真的卡住了」区分开。

相关代码在 `SessionHistory.swift`:

```
/// 记录每个会话最近 6 分钟的 CPU 采样,用来判断「连续满载」。
/// 只看瞬时值会把编译时的短暂 800% 当成异常,所以必须带时间窗。
private let keep: TimeInterval = 360 // 保留窗口
let sustain: TimeInterval // 连续多久算异常,默认 300s
let threshold: Double // CPU% 阈值,默认 200 (= 两个核)
```

**怎么降温**

就这两行:

```
taskpolicy -b -p <pid> # 降到 Background QoS → 系统调度到能效核
taskpolicy -B -p <pid> # 恢复
```

降到 background 之后进程还在跑,只是被调度到能效核,速度慢下来、发热下来,但进程不冻结、不中断、上下文不丢。

实测同一台机器,**70.6℃ → 44.9℃,降 25.7℃**。元凶是连着跑了 12 分钟的 Claude Code ,340%。

**几个实现取舍**

* 进程表用 **libproc 直扫**(`proc_listallpids` + `proc_pidinfo`),不 fork `ps`。fork 一次几十毫秒,扫全表太慢。
* 温度走 `IOHIDEventSystemClient` 私有符号,免 sudo 免 entitlement 。代价是**不能上 Mac App Store**,而且 macOS 大版本升级可能改传感器命名,需要容错。
* **只处理自己的进程**。别人的进程没权限降 QoS ,界面上会标出来但不动。
* **不杀进程是刻意设计**。AI 干活时被 kill ,前面跑的全白费,降速继续跑通常更划算。

**目前的状态**

macOS 14+,Apple Silicon ,菜单栏常驻。源代码公开:<https://github.com/luogao/manatee>;

想听听反馈,特别是:多 agent 并发的场景下,「连续 5 分钟 200%」这个判定阈值合不合适?我担心对短任务太钝,但又不想把编译误判进去。
https://www.v2ex.com/t/1252659#reply0