电脑卡了,然后呢?我想用 Rust 做一个 Mac 诊断小工具

电脑卡了,然后呢?我想用 Rust 做一个 Mac 诊断小工具

项目已开源:corn12138/hostwatch,使用 MIT 许可证。

电脑卡顿的时候,最烦的往往是搞不清它为什么卡。

打开活动监视器,数字很多:内存、缓存、交换空间,还有一长串进程。磁盘看起来也没满,但操作就是不顺。要关哪个应用?要不要清缓存?删点文件会有用吗?看完一圈,还是拿不准。

这次想做 Hostwatch,出发点就在这里:让电脑在出现资源问题时,给一点能看懂、能照着检查的提示。

我不想一上来就做很多功能。先做 Mac,Windows 留好接口。工具本身也得克制一点,毕竟做它的原因就是电脑已经不太流畅了。

这篇先记立项和最早的命令行原型。后面的采集细化、告警和界面,留到各自做完时再写。

第一个纠结:Rust 还是 Go

最开始就在这两个语言之间考虑。

Go 很适合这类小工具:写起来直接,并发和构建也方便。采几项系统指标、定时判断一下,本身并不需要多惊人的计算性能。如果只考虑尽快做出能用的版本,Go 完全说得通。

但这个工具以后可能一直挂在后台。我更在意的是,它挂着的时候究竟占多少内存,每隔一会儿会做多少工作。

Rust 没有垃圾回收器,在对象生命周期和分配上可以控制得更细。相比之下,Go 的 GC 需要在 CPU 开销和堆内存之间做权衡。这也是这次倾向 Rust 的主要原因。

不过,选 Rust 并不能直接得到一个省内存的程序。每秒扫描所有进程、不断保存历史、再给界面塞进一堆东西,用什么语言都可能变重。

所以这里没有"Rust 比 Go 快几倍"的结论。我们没有写两套相同实现做对照测试,也不准备拿一个空程序的占用来替完整产品背书。

这次先选 Rust,把低开销作为实现时要反复检查的约束。具体能压到多少,等程序跑起来再量。

原本以为,读几个数字就够了

最容易想到的做法,是设两条线:内存使用率超过多少就提醒,磁盘剩余低于多少就提醒。

磁盘这条还能继续讨论,内存那条很快就遇到了问题。

Mac 会用空闲内存缓存文件。看到内存用得多,不代表这些内存都被某个应用"占死"了。缓存本来就可能让下一次打开文件更快。如果工具只是盯着使用率,最后很容易变成一边系统在利用缓存,一边我们劝用户把它清掉。

swap 也类似。交换空间里有数据,不等于此刻正在频繁换入换出。只看一个存量,就说卡顿来自交换,同样有些草率。

这让第一版的判断收窄了不少:优先看内存压力;使用量和 swap 先作为背景信息展示。之后再补变化速率,观察一段时间内发生了什么。

还有一个小决定:读不到的数据就写"未知"。不能因为接口失败后拿到了默认值,就告诉用户"一切正常"。这种错误在普通页面里可能只是个数字不准,放在诊断工具里却会把排查方向带偏。

看着像少做了几条规则,其实是先把不敢下的结论拿掉。

磁盘也有个容易算错的地方

磁盘空间看起来更简单:总容量减去已用容量。

但到了 APFS,多个卷可能共享同一个容器的空间。如果枚举出几个卷,把它们的容量直接加起来,结果就可能重复计算。

第一版因此只读取启动数据卷,展示它的总容量和可用空间,不急着做"全机磁盘总览"。

这样能回答"现在是不是快没地方了",还回答不了"空间到底被谁用掉了"。后一个问题需要目录扫描,还会碰到权限、符号链接、扫描成本等事情,值得单独做。

而且空间足够,也不代表磁盘没有忙着读写。容量和 I/O 是两件事。工具要是只采了容量,就不能顺手把磁盘问题全部排除。

做到这里,反而更能理解为什么活动监视器里会有那么多栏目了。想把界面做简单,底下要先把这些区别弄清楚。

先在终端里跑,界面晚一点

第一版没有直接做菜单栏。

倒不是觉得界面不重要。这个工具最终要随手看一眼就能用,菜单栏很适合。但现在最需要确认的,是采到的数字有没有解释错,以及建议是不是有根据。

于是先留了两个入口:

sh 复制代码
./scripts/cargo.sh run --locked --release -- snapshot
./scripts/cargo.sh run --locked --release -- watch

一个取当前样本,一个持续观察。需要查应用时,再加 --processes 看可读取进程的 RSS 前五名。

进程排行默认关闭。列出来的虽然只有五个,底下仍然需要扫描进程列表;输出少,不等于采集成本也小。

代码也先按这几个部分拆开:平台采集负责拿数据,规则负责判断,命令行负责展示。以后加菜单栏时,可以复用前面的部分。

Windows 留了独立入口,但调用它会直接返回尚未实现。现在还不清楚的指标和平台差异,等实际适配时再处理,没有必要先填一组看起来像真的数字。

第一个把构建卡住的,居然是目录名

项目放在一个叫 monitor-Mac:Windows 的目录里。

名字很直白,Mac 和 Windows 都在里面了。结果 Cargo 开始构建后,报了这样一句:

text 复制代码
failed to join paths from `$DYLD_FALLBACK_LIBRARY_PATH` together
path segment contains separator `:`

问题出在冒号上。

macOS 的文件路径可以包含它,但动态库搜索路径又用它分隔多个路径。Cargo 把构建产物目录放进去时,就不能按原本的路径理解了。

最后没有改项目目录名,而是加了一个很小的脚本:当项目路径包含冒号,且没有显式指定 CARGO_TARGET_DIR 时,把产物放到用户缓存目录中。不同项目路径用校验值区分。

这个处理只影响当前构建,不需要改全局 Rust 配置。也解释了为什么上面的运行命令多了一层 scripts/cargo.sh。

原本在考虑语言运行时、内存口径,真正最先拦住编译的,却是给目录起名字时随手加的一个符号。这种事还是得跑起来才知道。

建议这件事,比采集更需要克制

把指标打印出来之后,下一步自然是:那该怎么优化?

这里很容易写出"清理缓存""释放内存""结束高占用进程"之类的按钮。看起来功能很明确,但工具到底知道多少?

一个进程占用高,可能正在正常处理工作。直接结束它,会不会让用户丢掉还没保存的内容?一个目录很大,也不代表里面的东西没用。

第一版因此只给检查路径和手动建议。例如内存压力升高,就提示去活动监视器里看对应应用,保存工作后再退出暂时不用的应用或标签。磁盘空间不足,就去系统的储存空间管理里审查大文件。

这些建议还比较基础,离"直接帮我找出原因"有距离。不过每一条至少能对应到当前采集到的证据。

测试也围绕这些容易误导人的地方展开:内存使用量和历史 swap 很高,但压力正常时,不该催着清理;压力未知时,不该判正常;内存和磁盘同时有问题时,两个都要展示。

这比多写几句优化文案更值得先做。

先把这一小步留在这里

走到最早的 CLI 原型这一步,语言选了,指标能读了,建议也有了最小的一版。编译和相关测试跑通,项目算是有了一个可以继续往下改的起点。

但这离一个值得常驻的工具还差不少。菜单栏好不好用、通知会不会烦、睡眠唤醒后数据会不会算错、运行一整天是否还稳定,都得继续做和验证。

尤其是占用。短时测量有参考价值,不能直接当成长时间使用的保证;CLI 的数字也不能拿来代表以后带界面的版本。

我希望最终遇到卡顿时,它能帮人缩小一点范围:是内存真的紧张,还是磁盘在忙,或者是某个应用持续占着 CPU。即使暂时不能替人解决,也要说清楚自己看到了什么。

下一步就从"这一瞬间的数字",继续往"一段时间里发生了什么"走。


文中涉及的技术说明,可以对照这些资料看:

记录日期:2026-10-19。本文只回顾立项与最早的 CLI 初始化阶段,不作为仓库最新功能清单。

相关推荐
GeminiJM3 小时前
从 0 到 1 学 Codex 源码:Rust 基础(一)
rust·源码阅读·cargo·编程入门·codex
恋喵大鲤鱼3 小时前
Rust 依赖包管理
rust
znnnk5 小时前
2026 前端工程化新纪元:Rust 重塑工具链,AI Agent 接管流水线
前端·人工智能·rust
gopher95111 天前
RUST:序列化与反序列化 serde + derive 详解
开发语言·后端·rust
遇码2 天前
流式调用中途断连别直接报错:瞬态故障识别、退避重试与中文根因诊断的搭建方法
大数据·人工智能·ai·rust·数据分析·大模型·agent
DongQiShanRen2 天前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
alwaysrun2 天前
Web之wasm-pack详解
前端·rust·web·wasm
DongQiShanRen2 天前
六根硬件映射(下):物理闭环五步法,抽一条已实装的看
大数据·jvm·人工智能·算法·rust·哈希算法·集成学习
Zenova EdgeOS2 天前
Rust TUI 实战:用 ratatui 构建工业边缘终端界面
开发语言·后端·rust·工业网关