今天翻到 Maneshwar 用 Rust 加 ratatui 写 ratatop 的开发记录,翻到第四天网络模块做完,四个面板能跑三个。本来只是随便看看,结果在速率显示那一小段被绊住了——好几个坑我自己写监控脚本的时候都踩过,顺手记一下。这篇不讲 ratatop 整体怎么建,只讲速率显示:单位怎么标、数字怎么格式化、上下行图表怎么共用一个比例。读完应该能避开我绕过的那几个弯。
先说单位。ratatop 的网络速率是同时给两个数的,比如 566 Byte/s 和 4.42 Kibps,这是刻意照着 btop 抄的。为什么非要两个一起显示?因为运营商卖带宽用比特每秒,下载工具和浏览器报进度用字节每秒,两边差 8 倍——你办的是 100 Mbps 的套餐,下载进度条上封顶也就 12.5 MB/s 左右。只显示其中一个,总有人拿着截图去问“我买的百兆为什么只有十二兆”,这种问题我见过太多次了。两个单位并排放在那,用户自己对账,比在文档里解释一百遍都管用。
然后是那个容易看漏的细节:单位写的是 Kibps,不是 Kbps。拆开看看的话,这里 ratatop 用的是 1024 进制做比特转换,所以老老实实标了 Ki。这个字符不是炫学,是在防混淆——基于 1000 的 kilobit 和基于 1024 的 kibibit 差了 2.4%,单看一次无所谓,但如果你拿 Kibps 的数去对运营商的 Kbps 宣传值,永远对不齐,然后开始怀疑自己的网卡。我自己以前写监控脚本的时候就混用过,对不上账还以为是采样周期算错了,查了半天。这条记下来了,下次应该用得上。
数字格式化也有一个坑。ratatop 的作者一开始用的是固定两位小数,比如 4.42、43.87、438.72。看起来挺整齐,实际跑起来会发现列宽一直在变——数值跨越数量级的时候,字符串长度一路涨,整个列就在那抖。他后来改成三位有效数字:
4.42
43.8
438.
这样不管数值落在哪个数量级,显示宽度都是稳定的。💡 小技巧:监控类工具的数字格式化,优先保证宽度稳定,再考虑精度好看。这个坑我也是绕了两次才躲开,第一次还以为是终端渲染的问题,调了半天渲染,其实格式化字符串改一下就完了。
图表这块更有意思。ratatop 把上传图表刻意上下翻转了:下载从中线向上长,上传从中线向下长,两个方向共用一条中线,看起来像流量从中心往外流的一个整体图形。这个设计我第一眼觉得是花活,后来在自己机器上对着看了一会儿,承认它确实比两个独立图表好读——上下行不对称的时候一眼就能看出来。
但比翻转更要紧的是比例。下载和上传必须共享同一个纵轴,比例由两边较高的那个峰值决定,另外设了 10 KiB 的下限,免得空闲时那点噪音流量被放大成满屏大图。为什么必须共享?反例很直接:如果让两边各自按自己的最大值缩放,一个 3 KB/s 的上传和一个 3 MB/s 的下载会被画成一样高。图形上看起来上下行很均衡,实际差了一千倍——这种误导比没有图表还糟,因为它看起来特别可信。
数据来源那边也有几个值得抄的判断。拿本机 IP 用的是 getifaddrs(3),而不是去翻 /proc——/proc/net/dev 只有流量计数,/proc/net/route 只给网关,/sys/class/net 里压根没有 IP 地址。一开始我也纳闷为什么非要用系统调用,把这几个文件挨个看一遍就明白了,/proc 里就没有一个现成放 IP 的地方。解析 /proc/net/dev 的时候还有个具体的坑:接收字节数是第一个计数器,发送字节数是第九个,如果你顺手拿了第二个字段当上传速度,拿到手的是收到的包数。这种错不报异常,就是数字看起来不对劲,特别隐蔽。
接口选择那段的逻辑也老实:n 和 b 键循环切换,列表里滤掉 loopback,默认选 /proc/net/route 里默认路由对应的接口,选不出来就回退到流量最大的那个。一层一层有兜底,没有哪个环节是“假设用户会自己选对”的。
划重点:第一,速率显示两个单位并排给,比特和字节各留一个,用户自己会对账;第二,用 1024 进制就标 Ki,别让用户拿着 kibibit 去对 kilobit;第三,数字格式化先保证宽度稳定,三位有效数字比固定小数位实用;第四,上下行图表必须共享纵轴比例,各自缩放等于画一张看起来很可信的假图。ratatop 下一步要做进程面板,作者自己说那是四个面板里工作量最大的,要挨个读 /proc/[pid]/stat 算 CPU 增量——那一段估计又是好几篇踩坑笔记的量,我等着看。你可以拿手头任何一个监控输出对照着检查一遍上面这四条,大概率能查出至少一处该改的。