🌐 English
Windows 桌面系统监控悬浮窗:透明置顶、托盘常驻,实时显示 CPU、内存、GPU、磁盘、网络与进程占用,支持多套显示风格预设与分模块自定义。
- ⚡ CPU:占用率环形图 + 实时折线曲线 + 处理器型号
- 💾 内存:占用率进度条 + 已用 / 总量(GB)
- 🎮 GPU:每块显卡一行,显示型号、占用率、显存占用
- 💽 磁盘:容量占用率(默认取最满的盘,卡片上标出盘符与剩余空间;可在设置里指定显示哪个盘)+ 实时读写速度(单位按量级自动换算:KB/s → MB/s → GB/s)
- 🌐 网络:实时下载 / 上传速度(单位按量级自动换算:KB/s → MB/s → GB/s)
- ⚙️ 进程:内存占用 Top 8
- 🎨 显示风格:4 套预设 + 分模块自定义
- 📌 系统托盘:显示 / 隐藏、置顶 / 取消置顶、打开设置、退出
- 📊 托盘显示:把最多 4 个指标(CPU / 内存 / GPU / 网络)的数字直接画在托盘图标上
- 📝 托盘文字:在任务栏托盘图标左边贴一行文字,如
CPU:60% || GPU0:30% || 内存:25% - 🕹️ 游戏模式:屏幕角落一层小字,固定显示
CPU / GPU0 / GPU1 / FPS四项;颜色、字号、横竖排列都能改,位置支持右上 / 左上 / 拖动自定义;窗口点透,不挡游戏操作 - 🎯 帧率(FPS):游戏模式里显示真实采集的帧率——后端订阅系统的画面呈现事件,覆盖 D3D11 / D3D12 / Vulkan 等一切走 DXGI 呈现路径的程序;需要管理员权限,普通权限下显示
— - ⌨️ 全局快捷键:
Ctrl + Alt + M切换显示 / 隐藏
📝 磁盘卡片上的百分比是「容量占用率」,不是任务管理器里那种「活动率 / 忙碌时间」。默认取所有本地盘中最满的那个,卡片标题会标出是哪个盘(如
磁盘 F:),右下角同时给出该盘剩余空间。旁边的「读 / 写」才是吞吐量,单位按量级自动换算(KB/s → MB/s → GB/s,与网络卡同一套换算)——所以「87% 占用 + 读 0.0 KB/s」并不矛盾,意思是「F 盘快满了,但此刻磁盘完全空闲」。想固定看某个盘(比如系统盘
C:),在设置里把「显示磁盘」从「自动(最满的盘)」改成对应盘符即可,改完立刻生效。切换后卡片标题、百分比、剩余空间、读写速度会一起跟着换到那个盘。另外,Windows 上那些没有盘符的恢复分区(
WINRETOOLS、Image、DELLSUPPORT之类)不会被计入,否则它们动辄 90%+ 的占用会一直霸占这个数字。
| 层 | 技术 |
|---|---|
| 前端 | HTML + CSS + TypeScript + Vite |
| 桌面框架 | Tauri 2 |
| 后端 | Rust(sysinfo + DXGI / PDH 采集) |
| 帧率采集 | ETW 实时会话(订阅 Present 事件,覆盖 D3D11 / D3D12 / Vulkan) |
| 托盘 / 全局快捷键 | Tauri tray API / tauri-plugin-global-shortcut |
从 Releases 下载 sys_monitor_tauri.exe 单文件版,放到任意目录双击运行即可 —— 无需安装,配置与运行数据写在用户目录下。
📦 Release 上只发布这一个单文件。
系统监控_x.x.x_x64-setup.exe/系统监控_x.x.x_x64_zh-CN.msi安装包需要自己执行npx tauri build完整打包生成(见下)。
环境要求:
- 🦀 Rust(stable,含 MSVC 工具链)
- 🟢 Node.js 18+
- 🌐 WebView2(Windows 10/11 自带)
# 安装前端依赖
npm install
# 调试运行(编译 Rust 并打开窗口,支持热更新)
npx tauri dev# 只构建 exe(推荐)
npx tauri build --no-bundle
# 完整打包(exe + MSI / NSIS 安装包)
npx tauri build产物位置:
| 产物 | 路径 |
|---|---|
| 🚀 exe | src-tauri\target\release\sys_monitor_tauri.exe(单文件,可直接运行) |
| 📦 安装包 | src-tauri\target\release\bundle\ |
⚠️ 完整打包需要 WiX 工具链,首次构建会自动下载。
⚠️ MSI 的本地化语言固定为zh-CN(tauri.conf.json→bundle.windows.wix.language)。不要删掉这项:默认按en-US生成locale.wxl时代码页是 1252,装不下中文productName「系统监控」,light.exe会在写 MSI 摘要流时直接报错退出。🐚 Git Bash / MSYS2 用户注意:PATH 里的
/usr/bin/link.exe是 coreutils 的link,会顶掉 MSVC 的链接器,报link: missing operand after '\377\376'(\377\376是 UTF-16 BOM)。请在「x64 Native Tools Command Prompt for VS」中构建,或手动把 MSVC 的bin\Hostx64\x64放到 PATH 最前面并设好LIB。更多环境坑见docs/development-notes.md。
- 主窗口默认 320×480,可调范围 280×360 ~ 600×900,无边框、透明
- 标题栏整条可拖动;窗口四边与四角都有透明缩放热区,可自由拖拽缩放
- 主窗口位置可选:悬浮自由拖动 / 固定右上角 / 固定左上角(多显示器下以窗口所在显示器的工作区为基准,不会压到任务栏)。这一项在设置里叫「主窗口位置」,只作用于主窗口本身;游戏模式那几行指示器的位置是另一项(「指示器位置」)。
Ctrl + Alt + M:显示 / 隐藏主窗口(注册失败时——比如这个组合已被别的程序占用——不影响启动,只是该快捷键不可用)- 托盘图标右键菜单:显示 / 隐藏、置顶 / 取消置顶、打开设置、退出
点击托盘菜单「设置」打开独立设置窗口,修改后即时生效,主窗口无需重启。
「始终置顶」对主窗口和设置窗口同时生效:勾选时两者都浮在其他窗口之上,取消后两者都不再置顶(设置面板不会自己留在最上层)。
| 配置项 | 说明 |
|---|---|
| 📌 始终置顶 | 主窗口与设置窗口都保持在其他窗口之上 |
| 🚀 开机自启动 | 随系统登录自动启动 |
| 🚪 关闭行为 | 最小化到托盘 / 直接退出 |
| 📊 托盘显示 | 把指标数字画在托盘图标上;仅当「关闭行为 = 最小化到托盘」时才出现 |
| 📐 托盘指标 | CPU / 内存 / GPU0 / GPU1 / 网络,最多 4 个,按勾选顺序排进图标 |
| 📝 托盘文字 | 在任务栏托盘图标左边贴一行文字(如 `CPU:60% |
| 🎨 托盘文字项 | 同上的指标,最多 5 个,按勾选顺序从左到右排 |
| ✨ 数值闪动 | 数值变化时那一项闪一下(涨红跌绿),可关 |
| 🖌️ 托盘文字颜色 | 自动(跟随任务栏明暗)/ 深灰 / 浅灰 / 白 / 黑 / 蓝 / 绿 / 橙 / 红 |
| 🔠 托盘文字字号 | 9 ~ 20 |
| 🕹️ 游戏模式 | 屏幕角落显示 CPU / GPU0 / GPU1 / FPS;仅当「关闭行为 = 最小化到托盘」时可勾选,选「直接退出」时置灰并自动取消勾选 |
| 🖌️ 游戏模式颜色 | 默认(亮绿)/ 白 / 黑 / 红 / 自适应(跟随系统明暗) |
| 🔠 游戏模式字号 | 10 ~ 36 |
| 🧭 游戏模式排列 | 水平(一行)/ 纵向(一列) |
| 📍 指示器位置(游戏模式) | 屏幕右上角 / 屏幕左上角 / 自定义(可拖动定位);只移动那几行指示器,与主窗口位置无关 |
| 🪟 主窗口位置 | 悬浮自由拖动 / 固定右上角 / 固定左上角 |
| 🎨 文字颜色 | 7 种预设 |
| 🔠 字号 | 10 ~ 18 |
| 🎭 显示风格 | 4 套预设 + 5 个模块的下拉自定义 |
| 💽 显示磁盘 | 磁盘卡显示哪个盘:自动(最满的盘)或指定盘符;下拉选项按本机实际磁盘动态生成 |
| ⏱️ 刷新频率 | 采集间隔:200 毫秒 / 500 毫秒(默认)/ 1 秒 / 2 秒 / 5 秒 |
配置文件位于 %APPDATA%\com.duan.sys-monitor\settings.json,字段带默认值,旧版本配置文件向前兼容(缺字段自动补默认值,不会因为多/少一个字段就整份重置)。
保存走「先写临时文件再改名」的原子替换,中途断电 / 进程被强杀都不会留下半截 JSON。万一配置文件确实损坏(解析不了),启动时会把它改名成 settings.json.bad 留档,再用默认值启动 —— 不会一声不响地把旧文件覆盖掉。
磁盘卡的盘符由 diskTarget 控制:
{
"diskTarget": "auto"
}| 取值 | 含义 |
|---|---|
"auto" |
默认。自动跟随所有本地盘中最满的那个 |
"C:" / "D:" … |
固定显示该盘。盘符取自设置窗口「显示磁盘」下拉框里的选项;若该盘当前不存在(拔了移动硬盘),会自动退回 "auto",卡片不会变成 0% 空壳 |
设置 →「显示风格」,四个预设按钮一键整套切换:
| 预设 | 布局 | 说明 |
|---|---|---|
| 🌱 默认 | 单列 | CPU 圆环 + 曲线,内存 / GPU / 磁盘进度条,网络双指标卡片 |
| 📈 曲线 | 单列 | 所有模块迷你折线图,便于观察趋势 |
| ✂️ 极简 | 单列 | 每个模块压缩为一行「名称 + 数值」,占用桌面空间最小 |
| 🔲 双列 | 双列网格 | 模块两列排布,窗口变矮,适合贴屏幕侧边 |
分模块自定义:每个模块有独立下拉框(CPU:圆环 / 迷你曲线 / 进度条 / 纯文字;内存、GPU、磁盘:进度条 / 迷你曲线 / 纯文字;网络:默认卡片 / 纯文字 / 迷你曲线)。改动任意下拉即进入「自定义」模式,点击预设按钮则整套套用。磁盘读写与网络速度的数值与单位都一起自动换算(KB/s → MB/s → GB/s),三种样式共用同一套换算。
磁盘模块还多一个「显示磁盘」下拉框,用来指定磁盘卡显示哪个盘——它和显示风格无关,改它不会把风格切成「自定义」。同理,「刷新频率」也属于性能选项,改它同样不会影响显示风格。
{
"autoStart": false,
"closeAction": "toTray",
"trayStyle": "simple",
"desktopMode": "floating",
"textColor": "#FFFFFF",
"fontSize": 12,
"alwaysOnTop": true,
"stylePreset": "default",
"layout": "list",
"cpuDisplay": "ring",
"memDisplay": "bar",
"gpuDisplay": "bar",
"diskDisplay": "bar",
"diskTarget": "auto",
"refreshInterval": 500,
"netDisplay": "stats",
"trayDisplay": false,
"trayItems": [],
"trayText": false,
"trayTextItems": ["cpu", "mem"],
"trayTextColor": "auto",
"trayTextFont": 12,
"trayTextFlash": true,
"gameMode": false,
"gameColor": "default",
"gameFont": 16,
"gameLayout": "horizontal",
"gamePosition": "topRight",
"gameX": 24,
"gameY": 24
}合法取值:
| 字段 | 取值 |
|---|---|
closeAction |
toTray | exit |
desktopMode |
floating | topRight | topLeft |
textColor |
任意十六进制色值(界面里给了 7 个预设) |
fontSize |
10 – 18 |
stylePreset |
default | curve | minimal | cards | custom |
layout |
list(单列)| grid(双列) |
cpuDisplay |
ring | spark | bar | text |
memDisplay / gpuDisplay / diskDisplay |
bar | spark | text |
diskTarget |
auto(最满的盘)| 盘符,如 C: |
refreshInterval |
200 – 5000(毫秒),超范围会被夹到边界值 |
netDisplay |
stats | text | spark |
trayDisplay |
true | false —— 是否把数字画在托盘图标上(界面里只在 closeAction: "toTray" 时提供) |
trayItems |
cpu | mem | gpu0 | gpu1 | net(gpu = gpu0 的旧写法),顺序即摆放顺序,最多 4 个 |
trayText |
true | false —— 是否显示任务栏文字条 |
trayTextItems |
取值同 trayItems,顺序 = 从左到右,最多 5 个 |
trayTextColor |
auto | 任意 #RRGGBB |
trayTextFont |
9 – 20(px),超范围会被夹到边界值 |
trayTextFlash |
true | false —— 数值变化时那一项闪一下 |
gameMode |
true | false —— 是否显示游戏模式浮层(需要 closeAction: "toTray",否则会被强制落回 false) |
gameColor |
default(亮绿)| white | black | red | auto |
gameFont |
10 – 36(px),超范围会被夹到边界值 |
gameLayout |
horizontal | vertical |
gamePosition |
topRight | topLeft | custom |
gameX / gameY |
浮层左上角的物理像素坐标;仅在 gamePosition 为 custom 时生效 |
trayStyle 是历史遗留字段,只为兼容旧配置保留,前端不再发送它。
勾上「托盘显示」并选好指标后,托盘图标会变成一块数字牌,指标越少、字越大:
| 选了几个 | 排布 | 字大概多大(20px 图标) |
|---|---|---|
| 1 个 | 占满整块 | ≈17px,最清楚 |
| 2 个 | 上下两行 | ≈12px,很清楚 |
| 3~4 个 | 左上 / 右上 / 左下 / 右下 四角 | ≈7px,能看但挤 |
按勾选顺序排(先勾的排前面),最多 4 个。设置面板里有同尺寸的放大预览。
{
"trayDisplay": true,
"trayItems": ["cpu", "mem", "gpu0", "net"]
}trayItems 取值 |
指标 | 图标上显示 | 悬浮提示 |
|---|---|---|---|
"cpu" |
CPU | 占用率整数(23) |
CPU 23% |
"mem" |
内存 | 占用率整数(62) |
内存 62% |
"gpu0" / "gpu1" |
第 1 / 第 2 块显卡 | 利用率整数(45) |
GPU0 45% |
"gpu" |
同 gpu0(旧配置的写法,仍然认) |
同上 | GPU 45% |
"net" |
网络 | 下载速率:< 1024 KB/s 取整(999)、1024~10239 显示 4.2M、再快显示 430M(1M = 1024 KB/s) |
网络 ↓4.2 MB/s ↑20.8 KB/s |
几点要知道的:
- 这一项只在「关闭行为 = 最小化到托盘」时出现。 选「直接退出」时进程不驻留,托盘图标一关就没,这些选项没有意义。
- 图标上只放得下 1~4 个字符。 Windows 通知区图标就是 16 px(125% 缩放 20 px、200% 缩放 32 px),四角里每格才 8~10 px,所以数字一律取整、不带单位、不带小数。完整数值、单位、上传 / 下载速率看托盘悬浮提示(鼠标停在图标上)——提示里的单位会跟着数值走(KB/s / MB/s / GB/s),上下行各标各的。
- 想要又全又清楚,用下面的「托盘文字」:图标是系统固定尺寸,塞不下标签和单位。
- 图标按
devicePixelRatio出图(100% 缩放画 16 px、125% 画 20 px),尽量不让系统再缩一次;数值没变时不重绘、不 IPC。 GPU1只有在机器上真有第二块显卡时才会显示(避免一个恒为 0 的假指标)。- 托盘上不再出现磁盘:容量占用率和读写速度只在主窗口的磁盘卡上看(那张卡本来就把百分比、读写、剩余空间都放在一起了)。老配置里若还留着磁盘项,打开一次设置会自动清掉。
- 网络口径与主窗口一致(都是下载速率上图标)。
- 取消勾选「托盘显示」或一个指标都不选,图标会还原成应用默认图标,不会停在最后一帧数字上。
想要 CPU:60% || GPU0:30% || 内存:25% 这样带标签、带单位的一行字,塞进托盘图标是不可能的(图标就 16~20 px)。所以这一项换了个做法:像 TrafficMonitor 那样,在任务栏托盘图标左边贴一个无边框小窗口显示这行字,任务栏在上下左右都能跟随。
它的宽度跟着内容走(换指标、改字号、数值位数变化都会重算一次),窗口右边固定贴着通知区、向左生长,所以数字变长时是左边在动、文字本身不会在窗口里漂。刻意不预留宽度:预留会让低速值(↓0.4KB/s)在标签后面空出一大块,代价更难看。
它和「托盘显示」一样,整块只在「关闭行为 = 最小化到托盘」时出现,选「直接退出」时既看不到这一项、文字条也不会显示(属于驻留托盘模式的功能)。
{
"trayText": true,
"trayTextItems": ["cpu", "gpu0", "mem"],
"trayTextColor": "auto",
"trayTextFont": 12,
"trayTextFlash": true
}| 字段 | 说明 |
|---|---|
trayTextItems |
与 trayItems 同一套取值,顺序 = 从左到右,最多 5 个(= 全选);没装第二块卡时 gpu1 自动跳过。缺字段时默认 ["cpu","mem"](不是空——空列表等于什么都不显示) |
trayTextColor |
"auto"(默认,跟随任务栏明暗自动选深/浅字)或任意 #RRGGBB |
trayTextFont |
字号,9 ~ 20,超出范围会被夹到边界值 |
trayTextFlash |
默认 true。数值变化时那一项闪一下:涨了闪红、跌了闪绿(A 股习惯),600ms 内淡出。只闪背景不动文字颜色,深/浅任务栏都看得清 |
几点要知道的:
- 写法是刻意压紧的:字间距 0,数字紧跟单位(
1.0KB/s而不是1.0 KB/s),冒号用半角。数值按自然宽度排、不预留,所以标签后面不会空出一块;代价是数字进位(9↔10、99↔100)时那一段会挪 7px 左右。悬浮提示里保留完整写法(网络 ↓4.2 MB/s ↑20.8 KB/s)。 - 分隔符(默认
||)与它的样式(opacity: 0.35+ 常规字重 = 浅灰细线)都在源码里,不在设置界面: 分隔符是src/main.ts顶部的TRAY_TEXT_SEP(改这里即可,旁边的注释写了它被引用的两处都会自动跟随); 颜色/粗细是src/styles.css里的.tt-sep(想让它和数字一样实:opacity: 1; font-weight: 600)。 两者都需要重新构建(npx tauri build --no-bundle)才生效。 - 设置里有「文字条状态」一行,实时显示后端判定:
已贴在任务栏上/未显示:任务栏处于自动隐藏/未显示:前台是全屏应用/未显示:开关没开…。勾了却看不到时先看它,不用对着任务栏猜。 - 文字条不挡任何点击(点透),也不进任务栏 / Alt+Tab。点在它上面的点击会落到任务栏,任务栏会顺势把自己顶到最前面 —— 文字条每轮巡检会把自己搬回任务栏前面,所以点完它照样在。
- 任务栏自动隐藏、或前台是全屏应用(全屏游戏、视频)时会自动隐藏,不会压在别人的全屏画面上。
- 只跟随主任务栏(
Shell_TrayWnd);副屏任务栏不显示。 - 落点分两档巡检:每 40ms 只比一次窗口 z 序(点任务栏后那行字会被任务栏盖住,这一档负责立刻搬回它前面,所以点一下不会「闪没」),约 320ms 才完整重算一次落点。改任务栏位置、改缩放、插拔显示器都会自动跟上。
- 颜色默认
auto:读的是系统的「Windows 模式」(SystemUsesLightTheme),浅色任务栏配深字、深色任务栏配浅字。 - 设置里的预览是照着任务栏画的:左边文字条、右边托盘图标与时钟示意,底色跟着任务栏明暗,字号按当前设置,数值用的也是实时数据(所以预览里的闪动就是任务栏上的闪动)。
打游戏时想余光扫一眼帧率和显卡占用,不需要整块监控面板。勾上「游戏模式」,屏幕角落就会出现一行小字,只有 CPU / GPU0 / GPU1 / FPS 四项——刻意不多塞指标,它就是给游戏场景准备的。
{
"gameMode": true,
"gameColor": "default",
"gameFont": 16,
"gameLayout": "horizontal",
"gamePosition": "topRight",
"gameX": 24,
"gameY": 24
}| 字段 | 说明 |
|---|---|
gameMode |
开关。只在「关闭行为 = 最小化到托盘」时可勾选 |
gameColor |
"default"(亮绿 #39FF14)/ "white" / "black" / "red" / "auto"(跟随系统明暗自动挑深/浅字) |
gameFont |
字号,10 ~ 36,超出范围会被夹到边界值 |
gameLayout |
"horizontal"(一行)/ "vertical"(一列) |
gamePosition |
"topRight" / "topLeft" / "custom"(界面上分别写作「屏幕右上角 / 屏幕左上角 / 自定义(拖动定位)」) |
gameX / gameY |
只在 "custom" 下有意义:拖动定位时记录下来的左上角坐标(物理像素) |
每项写法和托盘文字一致:标签 + 空格 + 数值(CPU 23%、GPU0 45%、FPS 144),横向排列时各项之间是等距间隔,不带分隔符。
几点要知道的:
- 联动:这一项和「托盘显示」「托盘文字」同性质,都属于驻留托盘模式的功能。把「关闭行为」手动改成「直接退出」时,它的勾选框会立刻置灰 + 自动取消勾选(后端也会强制把
gameMode落成false),已经贴在屏幕上的那行字同时收走——不会留下一个没有托盘托底、关不掉的悬浮层。 - 默认亮绿是给游戏画面挑的:深色场景里最跳。白 / 黑两色给浅色或明亮场景兜底,
auto跟随系统「Windows 模式」,口径和托盘文字条完全一致。 - 整层点透:这行字不吃任何鼠标事件,所以不会挡住游戏里的点击、拖拽、开镜;它也不进任务栏、不进 Alt+Tab。只有在你进入「拖动定位」时才临时接管鼠标(不拖了立刻恢复点透)。
- 位置:右上 / 左上按主显示器计算,统一留 16px 边距。把「指示器位置」选成**「自定义(拖动定位)」时会直接进入可拖状态**——那行字会跳到配置里记着的自定义坐标、带上虚线边框,按住它(鼠标落在那几行字上即可,不用挑缝)拖到目标位置;停手约 1.5 秒后自动落定,坐标写回配置,下次进游戏还在原位。落定后重新变回点透。
- 副手:设置里那一行的「开始拖动」按钮同样能进入可拖状态——切换过自定义坐标、或者中途点「结束拖动」提前收工时用它重新武装。
- 若武装后一直没拖过(比如选了自定义又忘了拖),30 秒后自动结束,把点透还给游戏;想提前结束就点「结束拖动」。
⚠️ 这一项只管那几行指示器,和主窗口位置无关。 两者都叫得出「右上角 / 左上角」,所以设置里刻意分开命名并隔开放:指示器是「指示器位置」(在游戏模式块内),主窗口是「主窗口位置」(在最上面的主窗口选项里)。实测确认改「指示器位置」时主窗口纹丝不动(改前主窗口与指示器的矩形互不影响)。- 排列:横向是一行,纵向每项一行(适合贴屏幕侧边竖着看)。
- 纵向时名称与数值各自成一列:名称列按最宽的名称(
GPU0)定宽、数值列按最宽的数值(100%/ 三位帧率)定宽,于是CPU / GPU0 / GPU1 / FPS的左边缘对齐、它们下面数值的左边缘也对齐;顺带把「数值位数变化」对窗口宽度的影响也消掉了。 - 尺寸不跟着数值抖:每段都按「最宽可能出现」的文本预留(百分比按
100%、帧率按三位数留),所以 9↔10、99↔100 进位时窗口尺寸不变、文字不会左右跳。 - 没装第二块显卡时不出现
GPU1,只留三项——避免一个恒为 0 的假指标。FPS则永远保留。 - 设置里有一行「游戏模式状态」(和「文字条状态」同一套路),直接报后端判定:
已显示/拖动定位中:拖到目标位置,松手 1.5 秒后自动记住/拖动定位中:按住那几行字拖到目标位置(未拖动时 30 秒后自动结束)/等待前端测量文字尺寸…/未显示:开关没开,或「关闭行为」不是最小化到托盘/未显示:取不到主显示器。勾了却没看到那行字时先看它。 FPS是真实采集的:后端订阅系统的画面呈现(Present)事件,覆盖 D3D11 / D3D12 / Vulkan 等一切走 DXGI 呈现路径的游戏。它取的是正在出帧的那个程序——前台优先(游戏在前台时就是它),前台只是静态界面时退到最忙的那个(所以切出去看一眼设置,overlay 上仍是后台游戏的帧率);没有程序在持续出帧时就显示—,不会拿"静态窗口每秒重绘两次"冒充帧率。需要管理员权限:ETW 的实时会话只能由提权进程开启,普通权限下那一栏显示—,设置里的「FPS 采集」一行会写明原因、以及在算哪个进程。CPU / GPU0 / GPU1三项不需要任何额外权限。
{
"refreshInterval": 500
}单位毫秒,允许 200 ~ 5000,超出范围会被自动夹到边界值。改完立刻生效,不需要重启。
| 取值 | 适用场景 |
|---|---|
200 |
想看瞬时尖峰(编译、压测时的抖动)。代价是 CPU 占用最高 |
500 |
默认。流畅度与开销的平衡点 |
1000 / 2000 / 5000 |
只关心长期趋势,或者想尽量少占用资源 |
开销参考(本机实测,release 构建):每一拍的固定开销约 7.3ms,其中 networks.refresh() 独占 6.0ms——sysinfo 每次都要把所有网卡重新枚举一遍。折算下来:
| 间隔 | 采集约占一个核心 |
|---|---|
| 200ms | 3.7% |
| 500ms | 1.5% |
| 1000ms | 0.7% |
| 5000ms | 0.15% |
另外两点取舍:
- GPU 卡最快 500ms 更新一次。 GPU 走 DXGI + PDH,单次采集本身就要约 150ms(PDH 需要双拍热身),所以即使把刷新频率设成 200ms,GPU 卡也不会跟着跑那么快。
- 曲线窗口随刷新频率一起变。 迷你曲线固定保留最近 60 个点,所以 200ms 时曲线覆盖约 12 秒,500ms 时约 30 秒,5 秒时约 5 分钟。想「看更长时间的趋势」除了调慢刷新频率没有别的办法。
sys_monitor_tauri/
├── index.html # 主窗口与设置窗口共用(按 window.label 区分)
├── README.md # 中文说明(本文件)
├── README_EN.md # 英文说明
├── src/
│ ├── main.ts # 前端逻辑:骨架重建 + 数值就地更新、曲线历史缓存、设置同步、拖动 / 缩放
│ ├── styles.css # 样式(字号走 --font-size-base 变量;单列 / 双列布局)
│ └── assets/
├── docs/
│ └── development-notes.md # 开发经验笔记:实现取舍、调试确认过的细节、构建环境已知问题
├── src-tauri/
│ ├── tauri.conf.json # 应用配置(窗口、bundle、identifier、WiX 语言)
│ ├── capabilities/
│ │ └── default.json # 窗口权限声明(主窗口 / 设置窗口 / 文字条 / 游戏模式浮层)
│ ├── Cargo.toml
│ └── src/
│ ├── main.rs # 入口
│ ├── lib.rs # 命令注册、托盘、设置窗口、设置变更广播;在 Builder::run() 之前启动采集线程
│ ├── monitor.rs # 采集线程:预热 + 周期可配置的主循环、快照缓存、GPU 独立线程缓存
│ ├── gpu.rs # GPU 采集(DXGI 枚举 + PDH 计数器)
│ ├── fps.rs # 帧率采集(ETW 实时会话订阅 Present 事件,需要管理员权限)
│ ├── taskbar.rs # 托盘文字条的任务栏定位:找 Shell_TrayWnd / TrayNotifyWnd、全屏与自动隐藏判断
│ └── settings.rs # 设置结构体与 JSON 持久化
└── dist/ # 前端构建产物
冷启动不再看到「打开一两秒才出第一组数字」的窗口。做法是把采集线程的启动提前到 tauri::Builder::run() 之前:
Tauri 的 setup 钩子是在窗口(WebView2)创建之后才执行的,所以在 setup 里才起线程,会让「冷枚举 CPU / 初始化 PDH 计数器(~300ms)+ 基线热身(250ms)」和「WebView2 启动」串行叠加。改成提前启动后,预热与 WebView2 启动并行,等 setup 拿到 AppHandle 时数据已经就绪。
配套的三点:
- 采集线程在第一帧前先把
Snapshot写入SnapshotCache,前端不等snapshot事件,主动调get_snapshot拉一次。事件会附带单调递增的sequence,前端拒绝过期或重复帧,避免缓存里的旧帧把刚到的事件覆盖回去 - GPU 卡片用
get_gpu_info单独兜底:启动时先做一次「只枚举适配器」的 DXGI 枚举(~20ms),利用率和显存随后由后台线程补齐 - 网络 / 磁盘的速率用真实累计字节算差分;只用
usage()的相对值再做一次差分会被算成 0
实测延迟还需要在不同机器上确认:「不卡住一两秒」有理论依据(预热从串行改为并行),但具体的可见首帧时间取决于 WebView2 / 磁盘 / 防病毒软件,文档不替真实环境背书。
欢迎提交贡献。
- 🍴 Fork 仓库并新建特性分支。
- 📦 按「从源码构建」一节装好依赖。
- 🚀 用
npx tauri dev热更新调试。 - 🧹 Rust 侧保持
cargo fmt格式化,尽量不留cargo clippy警告。 - 📮 提交 PR,说明改动内容与动机。
较大的改动请先开 Issue 对一下方案。
📌 关于贡献的说明:本仓库为专有许可项目。你提交贡献即表示同意作者可依 LICENSE 的条款使用、修改并纳入该贡献,而不是作为开源发布。
本项目采用 PolyForm Noncommercial License 1.0.0(源码可用协议,非开源协议)。
🆓 免费:个人、非商业用途(学习、研究、测试、个人爱好等)可自由使用、修改与分发。
💼 商业授权:任何商业用途——包括企业/公司内网或生产环境部署、集成到对外销售的产品或服务、再分发收费、提供收费的托管/外包/订阅服务——均不在许可范围内,须在使用之前向作者申请商业授权。请在 Issues 说明使用场景与部署规模,作者会私下回复授权方案与报价。
本仓库的 v1.0.0 及更早版本按 MIT 许可发布(MIT 不可撤销);自引入本许可证起的新版本按上述条款授权。
完整条款见 LICENSE。