LOADING

加载过慢请开启缓存 浏览器默认开启

晨曦拾光

思绪万千

记录技术与生活

DRM 半瘫故障排查与修复总结:QXL 显卡 TTM 死锁

技术 2026/8/31 0

日期:2026-08-31
对象:VM 801(Arch Linux,内核 7.1.5-arch1-2)
结论:根因是 QXL 虚拟显卡 的 TTM 显存管理异常导致关机路径 drm_modeset_lock 死锁,连坐 systemd PID 1;修复为换用 virtio-gpu。

一、现象

  • 系统”半瘫痪”:Hermes 网关、SSH 都在跑,但 systemctl 全部挂死/超时
  • SSH 新会话登录失败,报”传输端点未连接”
  • 内核 hung task 报告:systemd:1 blocked for more than 1228s,栈在 drm_modeset_lock,进程状态 D(不可中断睡眠)

二、排查过程

  1. 先疑 sshd 未运行——拉起后连接恢复,但 systemctl 依旧瘫痪
  2. 怀疑 D-Bus/套接字缺失——实测四个关键套接字全部连通,排除
  3. 最终定位:/proc/1/wchan 显示 PID 1 卡在 drm_modeset_lock,hung task 阻塞时间 245s → 1228s 递增后被抑制

关键证据(journalctl -b -1)

时间 事件
8/8 01:50 起 TTM Buffer eviction failed 持续爆发:8/8 一天 5193 次、8/9 75 次,整个 boot 共 5608 次
8/31 05:31:40 root 从 172.16.15.17(宿主机网段)SSH 登录
05:31:51 执行 shutdown(logind: poweroff requested from client PID 289246)
05:35:26 起 TTM eviction failed 密集出现(当天 340 次),关机流程走显示路径
05:35:53 systemd PID 1 blocked,hung task 报告 21 条,1105s→1228s 后抑制
05:31–06:58 hermes 进程持续运行、网络存活——“看着活着”
06:58:53 VM 终结,07:00:22 重启恢复

三、根因

  • 本 VM 显卡为 QXL 虚拟显卡qxl 驱动,16M VRAM + 64M surface),非 i915
  • qxl 的 TTM 显存管理持续异常(eviction failed 从开机第二天即出现,5608 次)
  • 8/31 05:31 的 shutdown 关机流程(停 Graphical Interface、显存回收)触发 drm_modeset_lock 死锁
  • systemd PID 1 撞锁永久阻塞(D 状态,SIGKILL 无效),整个管理栈失效
  • 此前误判为宿主机 i915 驱动锁死——错误。宿主机 5 个 boot 全部干净,i915 从未是故障源

四、修复

qm shutdown 801        # 优雅关机(修复前该路径必卡死)
qm set 801 --vga virtio # qxl → virtio-gpu
qm start 801

五、验证(三层全部通过)

  1. 结构lsmodvirtio_gpu,无 qxl/dev/dri/card1 + renderD128;本 boot 内核日志 0 条 TTM/hung task/drm_modeset_lock
  2. 行为:完整关机-重启循环,优雅关机 10.7 秒 完成(修复前:卡死 87 分钟被迫强制重启),连续两次通过
  3. 基线:systemd running,hermes-gateway / sshd 均 active,日志零异常

六、后续

  • 本机内核 7.1.5 可随时 pacman -Syu 升级观察(独立于本次修复)
  • 其他 VM 扫描:仅 115 (Win-xp) 用 qxl,但 Windows guest 走厂商驱动、不经 Linux TTM 路径,无同类风险,维持现状
  • 若文档化监控:journalctl --since "1 day ago" | grep -cE "TTM.*eviction failed|hung task|drm_modeset_lock" 应持续为 0

附录:错误结论作废声明

  • i915.enable_guc=0:无效(VM 内无 i915)
  • modprobe.blacklist=i915:无效(同上)
  • “唯一路径 sysrq”:仅对物理机成立;VM 可直接 qm reset
阅读全文

逆向小米搜索框:给 com.android.quicksearchbox 换必应 + 改造跳转 Via

技术 2026/8/31 0

设备:小米 13(HyperOS),KernelSU root
目标:把搜索框的百度换成必应;把网页搜索从写死的 Mi 浏览器改跳到 Via
结果:引擎切换 + 跳转改造均生效,附带一键切换脚本

一、搜索引擎配置藏在哪

小米搜索(com.android.quicksearchbox)的引擎逻辑集中在 com.android.quicksearchbox.xiaomi.searchengine 包,配置有三级数据源:

  1. APK 内置默认assets/websearch-default/searchengineNew.json
  2. 本地缓存(云端下发落盘):/data/user/0/<pkg>/files/data/websearch/websearch-<区域>-<hash>/searchengineNew.json
  3. 云端接口POST https://api.browser.miui.com/global/search/api/searchEngine/forGlobalSearch(默认 30 分钟~1 小时刷新一次)

核心 JSON 结构:

{
  "data": {
    "updateIntervalMinutes": 30,
    "defaultSearchEngineMap": {
      "globalSearchSearchBox": "baidu",
      "globalSearchHotList": "baidu"
    },
    "searchEngineSceneMap": {
      "globalSearchSearchBox": {
        "searchEngines": [
          {
            "searchEngineName": "baidu",
            "searchUrl": "https://m.baidu.com/s?...&word={searchTerms}...",
            "title_zh_CN": "百度"
          }
        ],
        "homepageSearchEngineCount": 3,
        "resetSearchEngineData": false
      }
    }
  }
}

两个场景:搜索框(globalSearchSearchBox)和热搜榜(globalSearchHotList)。{searchTerms} 是关键词占位符。

用户的选择保存在 SharedPreferences searchEngine

Key 含义
current_engine 当前引擎名(默认 baidu
engine_click_count 是否手动切换过
client_scene_info 场景 → {sceneId, currentEngine, hasChanged}

二、把百度换成必应

思路:不动引擎名current_engine 保持 baidu,避免服务端逻辑漂移),只把本地缓存 JSON 中 baidu 条目的 searchUrl / 标题换成必应:

  • searchUrl: https://cn.bing.com/search?q={searchTerms}
  • 标题:必应 / 必應 / Bing
  • 图标:本地 PNG(Glide 不支持 ICO/SVG,注意选 PNG 源)

防覆盖是关键:配置目录和文件都加 chattr +i(immutable)。云端刷新写文件失败时不会清本地文件,也不会切换 hash 文件名。

chattr +i searchengineNew.json
chattr +i websearch-zh-CN-<hash>/

三、点击建议”没反应”的真相

改完必应后测试:点击搜索建议词毫无反应。排查发现网页搜索跳转在 V2/n1.java硬编码了 Mi 浏览器:

intent.setClassName("com.android.browser",
    "com.android.browser.fullsearch.FullSearchActivity");

而这台手机上 com.android.browser 根本没安装——startActivity 抛的异常被 catch 静默吞掉。也就是说:任何引擎都会无反应,跟换必应无关

四、改 smali:跳转 Via

不装 Mi 浏览器、也不做桥接 APK,直接改 QSB 的 dex:把 V2/n1.c() 方法整体替换为”对 Via 发标准 VIEW”:

.method public static c(Landroid/content/Context;Landroid/content/Intent;Ljava/lang/String;Ljava/lang/String;Ljava/util/HashMap;)V
    .locals 3

    if-eqz p2, :cond_0

    :try_start_0
    invoke-static {p2}, Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;
    move-result-object v0

    new-instance v1, Landroid/content/Intent;
    const-string v2, "android.intent.action.VIEW"
    invoke-direct {v1, v2, v0}, Landroid/content/Intent;-><init>(Ljava/lang/String;Landroid/net/Uri;)V

    const-string v0, "mark.via"
    invoke-virtual {v1, v0}, Landroid/content/Intent;->setPackage(Ljava/lang/String;)Landroid/content/Intent;

    const/high16 v0, 0x10000000
    invoke-virtual {v1, v0}, Landroid/content/Intent;->addFlags(I)Landroid/content/Intent;

    invoke-virtual {p0, v1}, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V
    :try_end_0
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

    :cond_0
    return-void

    :catch_0
    move-exception v0
    return-void
.end method

c() 的第二个参数恒为 {searchTerms} 替换后的完整 URL(仅被 K() 内部两处调用),替换安全。

工具链与部署(全程 root):

# Termux
pkg install apktool python
apktool d -f --no-res base.apk -o qsb_d   # 只解 smali
# Python 正则整块替换 V2/n1.smali 的 c() 方法
apktool b qsb_d -o qsb_new.apk           # 只重编 smali,资源原样

# 部署:覆盖 base.apk + 删 oat 缓存
cp qsb_new.apk /data/app/<pkg路径>/base.apk
chown system:system base.apk && chmod 644 && restorecon
rm -rf /data/app/<pkg路径>/oat
am force-stop com.android.quicksearchbox

几个关键点:

  • base.apk 无 fs-verity(lsattrV 标志),可直接替换文件
  • 签名校验只在安装时发生;重启后 PMS 读 packages.xml 缓存,不重校验
  • 替换 dex 必须删 oat/(art/odex/vdex),否则 checksum 不匹配;首次启动 JIT 稍慢
  • 备份原 APK 和 websearch 目录到 /data/local/tmp/,出问题覆盖回去 + 删 oat 即回滚

五、一键脚本

把整个流程沉淀成脚本:engine(换引擎)、browser(换跳转目标)、restore(回滚)、status(体检)。换引擎时自动 chattr 解锁/加锁、修 SELinux context;换浏览器时基于原版备份幂等重建 APK。支持 via/chrome/edge,自定义引擎 URL 也行。

qsb-tool.sh status                 # 当前引擎/跳转目标/保护状态
qsb-tool.sh engine bing            # 换成必应
qsb-tool.sh browser via            # 跳 Via
qsb-tool.sh restore all            # 一键回滚
qsb-tool.sh refresh-backup         # OTA 升级后刷新 APK 备份

一键脚本下载qsb-tool.sh

OTA 后一键恢复ota-recover.sh——应用商店/系统更新后跑一次即可(自动检测:资源完整+已patch→只刷引擎;原版→重打补丁;资源缺陷(小米更新包 bug)→自动降级分区版再打补丁)。

依赖:root(KernelSU/Magisk)+ Termux 的 python3apktool(仅 browser 命令需要)。放到任意目录,bash qsb-tool.sh help 查看用法。

五·五、LSPosed 模块方案(进阶,推荐长期使用)

改 dex 的痛点:OTA 升级后失效、云端换 hash 绕过锁。如果你装了 LSPosed(Zygisk 版),可以直接 hook,不改任何文件、永久免疫 OTA

  • Hook 点 1com.android.quicksearchbox.xiaomi.searchengine.f.h(Context)(返回 searchUrl)→ 强制替换为 Bing URL
  • Hook 点 2V2.n1.c(Context, Intent, String, String, HashMap) 后置 → 拦截 Mi 浏览器跳转,重定向到已安装的浏览器(默认 mark.via

用法:下载 qsb-hook.apk 安装 → LSPosed Manager 里启用 → 作用域勾选 com.android.quicksearchbox → 重启 zygote(软重启或重启手机)生效。

⚠️ 注意:混淆类名 V2/n1 随 QSB 版本变化,本模块仅验证于 QSB 13.1.0.08242(HyperOS 4 / Android 17)。其它版本若 hook 失败,LSPosed 日志会提示 hook ... FAIL,不影响原功能。

LSPosed 模块下载qsb-hook.apk

六、经验总结

  1. 改系统应用优先改数据,其次改 dex,最后才重打包:配置 JSON + chattr 保护就解决了 90% 的问题。
  2. 无反应 ≠ 没调用:先看代码里异常是不是被吞了,再看目标组件是否存在。
  3. sed 处理 XML 转义串是坑& 是特殊符号),字符串替换一律用 Python。
  4. OTA 是最大敌人:升级会覆盖 /data/app 的修改,脚本里 refresh-backup + 重放流程即可恢复。
阅读全文

记一次 Android 17 升级后“内部存储文件全部消失”的排查与恢复

技术 2026/8/15 0

日期:2026-08-15
设备:小米 13(HyperOS / Android 17 beta),KernelSU root,数据分区 f2fs
结果:约 110.7GB、242,600 个文件、30,100 个目录,100% 恢复,零覆盖、零删除


一、现象

系统升级到 Android 17 后,/storage/emulated/0(内部存储)里几乎所有旧文件”消失”,只剩
AndroidDCIMDownloadMIUIPictures 等升级后应用重新生成的空目录。

而所有旧文件其实都在:

/storage/emulated/uncasefolded/0/

uncasefolded 是 vold 的内部目录(media_rw 权限),普通应用和文件管理器看不到它,
所以看起来像”文件全没了”。

二、根因

1. 大小写不敏感存储(casefold)迁移

Android 16/17 起,内置存储改为大小写不敏感(目录带 F casefold 标志,可用
lsattr 查看)。系统迁移时会把无法共存的大小写冲突文件搬进
/storage/emulated/uncasefolded/

本次升级中迁移异常(疑似小米 beta 的迁移中断/有 bug),把主用户几乎全部数据
搬进了 uncasefolded/0/ 却没有搬回。vold 迁移进程事后处于空闲状态,不会自动恢复,
需要手动搬回。

2. f2fs 项目配额(projid)——恢复时的拦路虎

手动搬回时,renameEXDEV (errno 18, Cross-device link)

  • 升级后应用新建的 Android/data/<包名> 目录带应用专属 project ID(如 20262),
    旧数据的 projid 是 0
  • f2fs 内核禁止跨 projid 的 rename,root 也无法豁免;
  • 注意:toybox mv 遇到 EXDEV 会静默退化为”复制+删除”,容易误以为 rename 成功。

3. 与 Magisk / KernelSU 模块无关

排查确认 /proc/mounts 挂载表干净,无模块干预痕迹。EXDEV 是文件系统层的
项目配额限制,Mountify 等挂载类模块不影响 rename 语义。

三、诊断命令

# 1. 看两侧内容
su -c 'ls -la /data/media/0/ /data/media/uncasefolded/0/'

# 2. 看目录的 projid 和 casefold 标志(F)
su -c 'toybox lsattr -p -d /data/media/0/Android/data/某包名 /data/media/uncasefolded/0/Android/data/某包名'

# 3. 确认没有可疑挂载点(排除模块/挂载因素)
su -c 'grep -E "uncasefolded|Android/data" /proc/mounts'

# 4. 测试跨 projid rename(预期 FAIL errno=18)
su -c 'touch /data/media/uncasefolded/0/.t; mv /data/media/uncasefolded/0/.t /data/media/0/Android/data/某包名/files/.t'
#     (mv 可能"成功",那是因为它自动退化成复制;用 strace/python 测试才是真相)

# 5. 验证"临时清零 projid 后可以 rename"(关键技巧)
su -c 'toybox chattr -p 0 /data/media/0/Android/data/某包名/files
       mv /data/media/uncasefolded/0/.t /data/media/0/Android/data/某包名/files/.t
       toybox chattr -p 原值 /data/media/0/Android/data/某包名/files'

四、恢复方案设计(数据安全第一)

  1. 先做全量清单:两侧目录的「相对路径 + 类型 + 大小」落盘留档,事后逐条核对;
  2. 只做原子无覆盖搬移:用 renameat2(RENAME_NOREPLACE),目标已存在时内核拒绝,
    杜绝覆盖;绝不使用会覆盖的 mv/cp
  3. 大小写冲突改名保留双方:从 uncasefolded 搬入的冲突项改名
    xxx (uncasefolded)(重名再加数字),两边都保留;
  4. EXDEV 处理:临时把源/目标父目录 projid 清零 → rename → 结束后恢复原值;
    待恢复列表持久化到 pending_projid.tsv,中途断电/被杀,下次运行会先恢复;
  5. 兜底:真·跨挂载点(如 .siexternal)无法 rename 时,文件走
    「复制→校验大小→fsync→无覆盖改名→删源」,目录走「重建→递归」;
  6. 结束逐文件校验:每个源文件都能按映射表解析到目标路径且大小一致、
    uncasefolded 剩余文件数为 0。

同分区搬移全部是 rename,瞬间完成、不占额外空间,权限/时间戳/SELinux 上下文
原样保留。

五、完整脚本 restore_merge.py

脚本本体约 14KB,作为附件提供,正文不再贴全文:

restore_merge.py

子命令:manifest 生成两侧清单、merge 合并、verify 校验、chattr 补 casefold 标志;
具体用法见下一节。

六、使用步骤

# 0. 前提:root(su),python3
#    把脚本放到 root 可执行的位置
su -c 'cp restore_merge.py /data/local/tmp/ && chmod 700 /data/local/tmp/restore_merge.py'

# 1. 生成两侧清单(必须在 merge 之前做,用于校验)
su -c 'python3 /data/local/tmp/restore_merge.py manifest'

# 2. 执行合并(可重复执行,幂等;中断后重跑即可)
su -c 'python3 /data/local/tmp/restore_merge.py merge'

# 3. 校验(期望:src 剩余文件数=0、problems=0、原dst文件消失=0)
su -c 'python3 /data/local/tmp/restore_merge.py verify'

# 4. (可选)给能加 F 标志的目录补 casefold 标志
su -c 'python3 /data/local/tmp/restore_merge.py chattr'

Termux 里 python3 的完整路径是 /data/data/com.termux/files/usr/bin/python3
其他环境直接用 python3

七、本机实测结果

项目 结果
源文件总数 242,600(约 110.7GB)
搬回并校验通过 242,600 / 242,600 ✅
uncasefolded 剩余文件 0(只剩 4.2MB 空目录壳)
原有新文件丢失 0
大小写冲突改名保留 1,542 处 → xxx (uncasefolded)
临时清零的 projid 172 个目录,全部恢复原值
迁移期间应用新写入的文件 43 个(EXTRA,属正常)

verify 报告中唯一一条 SIZE-MISMATCH 是相册应用正在使用的 SQLite -wal
日志文件被应用正常截断所致,并非数据丢失。

八、收尾

# 1. 重启一次手机,让媒体库重新扫描索引(相册/音乐等应用重新"看到"文件)

# 2. 查看所有因大小写冲突而改名的文件(双方都保留,自行取舍合并)
find /sdcard -name "* (uncasefolded*" -print

# 3. 确认一切正常后,删除 uncasefolded 空壳(建议先观察几天)
su -c 'rm -rf /data/media/uncasefolded'

九、注意事项

  • 恢复全程只做 rename(同分区搬移),不复制、不占额外空间、瞬间完成,
    权限/时间戳/SELinux 上下文原样保留;
  • 升级后应用新建的文件和目录不会被覆盖,原样保留;
  • 冲突改名的文件双方都在,没有任何数据被删除
  • 大量旧目录无法加 F(casefold)标志,因为内核要求空目录才能启用;
    这只影响大小写查找语义,不影响正常访问;
  • merge 中途被杀/断电:直接重跑即可(幂等,且会先恢复上次未还原的 projid);
  • 执行前建议先看 vold 是否还在做迁移(su -c 'top -b -n1 | grep vold'),
    确认空闲再动手。

十、进阶:为旧目录启用 casefold(大小写不敏感)

为什么恢复后很多旧目录加不上 F 标志

F 标志 = 目录级大小写不敏感。内核只允许「空目录」或「升级后新建的目录」
直接 chattr +F;旧目录里的目录项(dentry)是启用 casefold 之前创建的旧格式,
直接翻转会报 Directory not empty,即使目录里只有文件。

实测还发现两个关键点:

  1. 危险:目录里若已有大小写重名文件(如 A.txt / a.txt),直接 chattr +F
    会让其中一个文件”隐身”(无法再访问)!所以必须先查重名,有重名的目录整体跳过
  2. 跨目录 rename 的 EXDEV:内核检查「源 inode 的 projid == 目标父目录的 projid」。
    应用创建的文件本身带应用 projid(如 30463),搬到 projid=0 的暂存目录会被拒。
    解法:临时清零源 inode 的 projid,rename 后恢复(文件和目录都可 chattr -p)。

转换方案

对每个无 F 的旧目录:搬空到同分区暂存目录 → chattr +F → 搬回
(搬回时内核按新格式重建目录项)。每目录独立事务,搬出失败自动回滚;
崩溃/中断后下次运行自动从日志配对找回滞留的暂存目录。

完整脚本 normalize_casefold.py

脚本本体约 10KB,作为附件提供:

normalize_casefold.py

用法:su -c 'python3 /data/local/tmp/normalize_casefold.py'

实测结果

  • 第一轮:转换成功 20,684 个目录,失败 274 个(均为应用文件带 projid 的 EXDEV,
    数据完好、自动回滚);中断一次后自动找回 130 个滞留暂存目录,零丢失
  • 第二轮(修复源 inode projid 清零后):144 个全部成功,失败 0
  • 最终仅剩 5 个目录未转换(内含大小写重名文件,为防”隐身”按设计跳过):
    cn.kaiheila/filescom.zongheng.reader/filescom.tencent.hunyuan.app.chat/files
    com.tencent.lolm/filescom.tencent.qqmusic/files
  • 若想转换这 5 个目录,需要先手动改掉其中的大小写重名文件(双方都在,可自行取舍)

常用命令

# 查看目录是否已启用 casefold(含 F 即已启用)
su -c 'toybox lsattr -d /sdcard/某目录'

# 查看跳过的重名目录里有哪些冲突对
su -c 'ls /data/media/0/Android/data/cn.kaiheila/files | sort -f | awk "{n=tolower(\$0); if(seen[n]++) print}"'

十一、为什么 Android 16 没事、升级 17 才出事(根因溯源)

结论先行

Android 16 上小米通过内置的 casefolding_remover 服务关闭了大小写不敏感存储(casefold),
所以一切正常;Android 17 开始该特性被强制启用,升级时系统执行了启用迁移(数据搬进
uncasefolded),随后小米的兼容服务尝试把它”关回去”,但禁用失败Disabling failed),
数据卡在 uncasefolded 里没有搬回。

证据

本机系统属性(su -c getprop):

属性 含义
external_storage.casefold.enabled 1 casefold 被启用
ro.casefolding.adjusted 1 启用迁移已执行
persist.sys.casefolding.status Disabling failed 小米随后尝试”禁用”casefold,失败
init.svc.casefolding_remover stopped 服务跑完即停(oneshot),没有恢复动作

服务本体 /system/bin/casefolding_remover(Rust 实现,对应 AOSP 标准接口
android.os.casefoldingremover.ICasefoldingRemover),从二进制字符串可以看到它的
状态机与错误路径:

Enabled / Enabling / Disabling / Disabled
Enabling failed / Disabling failed
Failed to remove case
Attempting to move folder to dest (does not change casefolding)
persist.sys.casefolding.status
external_storage.casefold.enabled
persist.sys.casefold.enabled.override

时间线还原

  1. Android 16:casefold 被 Xiaomi 关闭(兼容自家应用),存储保持传统大小写敏感
  2. 升级 Android 17:casefold 强制启用,系统执行启用流程——把 /data/media/0 数据
    整体搬进 uncasefolded 暂存、给目录打 F 标志
  3. casefolding_remover 按小米策略尝试禁用 casefold(改回旧状态),
    中途失败(很可能败在本文第四/十章记录的同一个坑:f2fs 内核拒绝跨 projid
    的 rename,它的搬回操作没有兜底)
  4. 失败后系统彻底放弃:uncasefolded/0 的 mtime 停在升级当天 00:29,
    00:34 起应用开始重建空目录,vold 全程空闲——与观察完全吻合

与 root / Magisk 模块无关

挂载表干净、无模块干预痕迹;这是小米官方「启用→禁用」流程失败与 Android 17
强制启用 casefold 的组合结果。root 只是让我们得以手工完成系统没做完的迁移。

后续影响与建议

  • casefolding_remover 每次开机都会再跑一次(init 启动项)。它目前仍会失败;
    即使未来某次”成功”,也只会处理已空的 uncasefolded 壳、最多去掉部分 F 标志,
    不会丢数据,无需干预
  • 当前状态(casefold 启用)与恢复后的文件系统状态一致,保持不动即可
  • 若小米后续推送修复,persist.sys.casefolding.status 会变为正常值,可留意观察
阅读全文

Windows 桌面黑屏故障排查实录

技术 2026/7/31 0

一次「桌面黑屏但窗口正常」的排障记录。期间一度以为罪魁祸首是 NVIDIA 覆盖层,最终证实那只是干扰项——真正的主因是 Windows.UI.Xaml 宿主崩溃,导致 explorer 无法创建桌面外壳窗口(Shell_TrayWnd)

阅读全文

HFish 蜜罐数据库迁移实录:从 SQLite 到 MySQL 的踩坑与总结

技术 2026/6/30 0

背景

一台跑在京东云上的 HFish 蜜罐(2核 2GB),跑了大约 40 天,某天登录面板时提示”数据库错误”。SSH 上去一看,日志里铺天盖地的 database is lockedcontext deadline exceeded

数据库是 HFish 默认的 SQLite,文件体积 1.5GB,里面 file_attack 表 211 万行。蜜罐 7×24 接收全球扫描,每秒几十条写入需求,全部怼在 SQLite 那唯一一个写入槽上,崩盘是迟早的事。

阅读全文

从零搭建 Hexo 博客记录

技术 2026/5/27 0

一直想有个自己的小角落,记录一些技术笔记和生活碎片。折腾了一晚上,博客总算跑起来了,在这里简单记一下过程。

阅读全文
1
avatar
Serein

偶尔更新。
技术、阅读和摄影碎片。

总访问 -