爱意满满的作品展示区。
royluo

做了个菜单栏小工具:找出是哪个进程把 Mac 烤热的,把它挪到能效核继续跑

  •  
  •   royluo ·
    luogao · 4h 21m ago · 210 views

    跑多 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%」这个判定阈值合不合适?我担心对短任务太钝,但又不想把编译误判进去。

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1561 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 18:06 · PVG 02:06 · LAX 11:06 · JFK 14:06
    ♥ Do have faith in what you're doing.