问题
面板上「通知」图标点开时很卡:点击后要等约 3 秒弹窗才出现。只有通知弹窗这样,音量 / 日历 / 网络等其它弹窗都正常;桌面本身不卡。
实测证据
| 检查 | 结果 |
|---|---|
瞬时 CPU(每 0.1s 采 /proc/<pid>/stat 的 jiffies 增量) | 点开瞬间 plasmashell 烧满 1 个 CPU 核约 2 秒(两次点开 = 两次尖峰);kwin 峰值仅 18% ⇒ 合成器无责 |
| 是阻塞还是计算 | 计算。主线程在该窗口最大「空隙」仅 0.498s,其余全是亚毫秒级调用 ⇒ 不是等待 |
| 系统监视器观感 | 点击时该进程 CPU 从 0.1% 抬到 ~3% —— 与上一条同源(监视器显示的是平滑/均值,把尖峰摊平了) |
| QML 报错锚点 | Globals.qml:575: Unable to assign QString to int,每次打开弹窗恰好报一次 |
根因(上游源码定位)
plasma-workspace v6.6.6,applets/notifications/FullRepresentation.qml:51-63:
Connections {
function onExpandedChanged() { // 弹窗展开时
if (appletInterface.expanded) {
...
for (let i = 0; i < historyModel.count; i++) { // 遍历【全部历史】
let rowIdx = historyModel.index(i, 0);
historyModel.setData(rowIdx, true, ReadRole); // 逐条标记已读
}
}
}
}
⇒ 每次展开都对整个历史模型逐条 setData,每条触发一次 dataChanged + 委托重绘 ⇒ O(N) 次模型写入 + 大量重绘 ⇒ 烧满一个核约 2 秒。
这解释了全部现象:① 「积累一定内容后变慢」= N(历史总条数)在涨;② 「只有几条也慢」= 可见的几条 ≠ 遍历范围(N 是全部历史);③ 「只有通知弹窗」= 这段代码只在这里。
同路径另有上游类型 bug:Globals.qml:575 jobError: popup.jobError(声明为 int,实际给 QString)。
顺手修掉的另一个真问题:主题 colors 缺失导致 statx 风暴
排查中先发现:KDE 主题的可选文件 desktoptheme/<主题>/[variant/]colors 本机全缺 ⇒ 每次查找空扫整个 XDG_DATA_DIRS(88 个目录)。
| strace 实测(80 秒窗口) | 数值 |
|---|---|
statx 调用总数 | 1,375,763 次,其中 99% 是 ENOENT(找不到) |
| 单条路径被查 | …/default/translucent/colors 617,864 次 + …/default/colors 617,840 次 |
| 常态 / 打开弹窗瞬间 | ~500 次/秒 / 单次爆 8,400+ |
处置(已持久化 ✓):在 XDG_DATA_HOME(查找链第一位)补占位文件 ⇒ 实测 20 秒内 statx 归零。声明式落点 = plasma.nix 的 system.activationScripts.plasma-theme-colors(幂等,只补不覆盖)。
排查中走错的两条路(留档以免重蹈)
- 误判一:KDE 状态里的「残留引用」。卸掉 marktext/elisa 后,
ksmserverrc等四处仍留着指向旧 store 路径的条目,plasmashell 每次交互都在解析失败 ⇒ 清理后Failed to resolve归零。但这只清掉了真垃圾,不是卡的原因(相关 ≠ 因果)。 - 误判二:「CPU 平在 3.3%」。那是
ps给的进程启动至今平均值,2 秒尖峰被摊平。改用 jiffies 增量才看见 100% 核。
转折点是停止推测、上量化工具:strace 统计 → jiffies 瞬时采样 → 上游源码比对。
最终裁决(2026-10-16):根因不修,改用「手动清理」
关键事实:通知历史是纯内存态、不落盘(plasmashell 打开的文件里没有通知库)⇒ 开机时 plasmashell 尚未启动,登录后历史天然从零开始;睡眠唤醒不重启 plasmashell,也不影响它。所以「每次开机清零」本身就已自动成立,无需机制。
真正的痛点:一次长登录会话内历史越攒越多 ⇒ 弹窗越来越慢(N 在涨)。
| 候选方案 | 结论 | 原因 |
|---|---|---|
给 plasma-workspace 打 QML 补丁(只标记未读项) | 放弃 | 补丁已就位、并成功接入过 overlay;但需从源码重建 plasma-workspace(实测 20–30 分钟满 16 核),用户判定「连装一套 NixOS 都够了」,不值;且 KDE 版本一变就可能编不过 |
| 用户级 plasmoid 覆盖(家目录放一份改好的 applet) | 实测不可行 | 该 applet 由 plasma_add_applet(… QML_ARGS …) 构建,QML 编进模块 + C++ 支撑;Plasma 确实去读了用户目录(说明用户级优先成立),但包结构校验不通过、标记未出现 ⇒ 文件级覆盖不被采用 |
| 定时重启 plasmashell(systemd user timer) | 否决 | 没有「清空历史」的 DBus 接口,清空只能靠重启 plasmashell ⇒ ① 面板会闪(可见打断)② 定时器会变成「隐性常驻机制」,与睡眠唤醒纠缠(唤醒后可能补跑) |
| 接受现状 + 卡了手动清(当前采用 ✓) | 采用 | 零机制、零副作用;判据 = 变慢时右键通知图标 →「清除通知历史」即恢复 |
上游接口实测(本次一并记录):/org/freedesktop/Notifications 只有 CloseNotification(id) / Notify / GetCapabilities / Inhibit / UnInhibit / InvokeAction,没有「清空历史」类调用;/MainApplication 只有 Qt 通用方法。
当前运行时到底靠什么「不卡」(重要)
| 机制 | 是否生效 | 性质 |
|---|---|---|
| 主题 colors 占位 | 生效 ✓(声明式,rebuild 也在) | 持久,但它治的是 statx 风暴(全桌面通用开销),不是弹窗专属 |
| 通知历史为空 | 生效,但只是「现在空」 | 瞬时——这才是「打开很快」的真正原因;历史再涨,慢会回来 |
| QML 补丁 / 文件覆盖 / 定时任务 | 均未启用 | 见上表裁决 |
结论:现在不卡靠的是「历史是空的」,不是「修好了」。
复核命令
# 1) 点开弹窗时谁在烧 CPU(每 0.1s 采 jiffies 增量,100 jiffies/s = 1 核满载)
P=$(pgrep -f plasmashell | head -1)
while :; do awk '{print $14+$15}' /proc/$P/stat; sleep 0.1; done
# 2) 是否还有主题查找风暴(正常应接近 0)
sudo -n strace -f -e trace=statx -o /tmp/s.log -p $P -- sleep 20; grep -c desktoptheme.*colors /tmp/s.log
# 3) 主题 colors 占位文件在不在
find ~/.local/share/plasma/desktoptheme -name colors
# 4) 上游那一处是否已修(若有 ⇒ 升级 KDE 即自动解决)
# invent.kde.org → plasma/plasma-workspace → applets/notifications/FullRepresentation.qml
# 看 onExpandedChanged 里是否仍是「遍历全部历史逐条 setData」
边界与残留
- 本机制是上游 QML 的性能问题,不是本机配置错误;换机/重装同样会遇到(随会话内历史累积加重)。
- 唯一未做的待查:上游 master 版同一段是否已修(若已修 ⇒ 将来升级 KDE 自动解决,无需任何操作)。
- 补丁文件留档:
/etc/nixos/modules/system/laucher/plasma-notifications-readloop.patch(未启用;plasma.nix里留了「如何重新启用」的注释)。 - 用户级覆盖实验的残留已清理(备份见
~/.hermes/cache/scratch/notif-plasmoid-override-attempt/)。 - 另有一个与本次无关的既有报错:
plasma-desktop-6.6.6/…/org.kde.plasma.icontasks/缺 mainscript(清理前后都在),待日后再看。