手上有两台红米 Note 13 5G(型号 2312DRAABC,内部代号 gold,天玑 6080,8+256,HyperOS / Android 15)。这 SoC 没有 Linux 主线内核支持,刷不了原生 Linux 发行版,但 8G 内存 + UFS 256G + 能一直插着电——拿来当 7×24 家庭服务器正好:音乐库离线听、远程可运维、双机互备。

整个折腾历时一周,从解锁 bootloader 到音乐自动同步上线,中间还出了双机重启卡死的事故。这篇把过程和结果完整记一下。

第一步:解锁 bootloader(最危险的一步)

小米官方解锁等待期长、限制多,走的是 Jz8Root 的 MTK RPMB 硬件解锁路线——通过 BROM 用 preloader 直接写 seccfg。但第一原则是先备份一切:动任何东西之前,先把所有 NV 分区(nvram / nvdata / nvcfg / persist / protect1 / protect2 / seccfg / lk / RPMB)用 mtkclient 完整导出来。

nvram/nvdata 丢了就是丢 IMEI 和基带。每台 ~536MB 的备份是砖机或丢 IMEI 时的唯一保险,必须带走。

进 BROM 有一套完整仪式,其中有一条铁律:必须用 USB 2.0 口——USB 3.0 口会静默损坏写入,没有任何报错:

  1. 手机完全关机
  2. 同时按住音量+ 和音量−
  3. 插 USB 线,保持按住 15 秒以上,屏幕必须全黑
  4. 电脑端出现 0e8d:0003

电脑侧也有坑:要先停掉 ModemManager(它会抢占 MTK 串口)、卸载 option / usb_wwan 驱动。看到 mtkclient 不认的 0e8d:20ff,基本就是这两个没做干净。

BROM 通了之后,流程其实不复杂:

scan_lk.py          # → COMPATIBLE_FULL,符合 Path A 硬件解锁条件
da rpmb e ...       # 擦除 RPMB magic(这台定位在 sector 65504)
da seccfg unlock    # 写入解锁

两个细节:擦掉 RPMB magic 之后、seccfg unlock 之前绝不能碰电源键(preloader 会在任何启动时重写 magic);解锁后首次开机必须擦掉 userdata/metadata,否则直接卡在 dm-verity 的"设备已损坏"界面。fastboot getvar unlocked 返回 yes,两台手机正式离开出厂状态。

第二步:Root——这个机型没有 init_boot

这台机器的特殊之处:没有独立 init_boot 分区,ramdisk 放在 vendor_boot 分区里的 init_boot.cpio。所以 Magisk 标准的"修补 init_boot"流程走不通,得改刷 vendor_boot 补丁镜像,而且必须 Magisk v30.7 起步(PR #9370 才加入 vendor_boot 支持),旧版会刷入成功但 root 不生效。

流程:

1
2
3
4
# 1. dump 原厂 vendor_boot(root 前只能走 BROM,root 后直接 dd)
# 2. push 到手机,Magisk app → 安装 → 选择并修补一个文件
# 3. 拉回刷入
fastboot flash vendor_boot_b magisk_patched-vendor_boot.img

还有两个坑:

  • 本机的提权入口是 /debug_ramdisk/magisk su/sbin/su/system/xbin/su 都不存在——别据此误判 root 失效
  • 开机后第一次 su 请求可能被自动拒绝并记住,要到 Magisk 超级用户列表里手动把 shell 改成「允许」

第三步:裁 HyperOS 冗余(内存从 78MB 到 5G)

出厂状态 HyperOS 只给可用内存留了 78MB。裁剪之后:

指标裁剪前裁剪后(重启稳定)
Mem used5366 MB2456 MB
Mem free2212 MB5122 MB
Swap used186 MB0 MB

具体做了这些:

  • 禁用约 100 个系统包(全部 pm disable-user,可逆):小爱/AI 全家桶(voiceassist、aiservice、aiasst…)、广告统计(systemAdSolution、analytics…)、应用商店/游戏中心/快应用、云同步/车联/投屏、支付钱包虚拟 SIM、各类测试工具和日志上报
  • 卸载 21 个预装垃圾pm uninstall -k --user 0):淘宝、闲鱼、拼多多、美团、微博、抖音、快手、今日头条、番茄小说、腾讯视频……保留支付宝、高德、WPS、米家
  • hosts 广告拦截:Magisk hosts 模块(yhosts 列表 6428 条,含小米系广告域)
  • 关掉负一屏和上滑资讯流(这俩会被系统偷偷重新启用,要重新禁)

踩的坑:

  • 第一轮误伤了天气和主题商店,广告 hosts 又误拦了天气域名。后来恢复 4 个包 + 给 hosts 模块加白名单
  • pm enable 状态落盘有延迟,启用后立刻重启会丢失,要等 20 秒再重启
  • 漂移:重启 5 次后 HyperOS 把 24 个包重新启用了。给 trim-hyperos.sh 加了 verify 模式(只报告不改动)+ KEEP 例外表,以后每次重启后跑一次 verify 自查

第四步:服务器化

目标是 7×24 随时能连、能干活:

  • Debian chroot:chroot-distro 模块装 Debian 12 bookworm arm64(rootfs 只有 74MB),里面跑 sshd:2222 + shellinabox(Web 终端)+ glances(监控面板)
  • 保持唤醒:写 gold_server wakelock,防熄屏后 deep doze 挂起系统、掐网络
  • 网络调优:TCP BBR + fq qdisc;CPU 最低频锁定(MTK power HAL 开机后会重置,脚本每 30 秒重写 5 分钟对抗);GPU 从锁死的 dummy 调度换到 simple_ondemand
  • WiFi 看门狗:每 60 秒 ping 网关,连续 5 次失败自动 svc wifi disable/enable 重连——前两天数据链路抽风了 3 次,全部被它自愈
  • 80% 充电上限:ACC 模块的守护进程在这个机型上不工作(97% 还在充),自己写了脚本直接写 input_suspend:≥80% 停充、≤60% 恢复。长期插电当服务器,电池健康是刚需

双机重启卡死事故

第二天 13:45,两台手机重启后同时卡死:bootanimation 一直转、sys.boot_completed 为空、zygote 状态 restarting、crash buffer 里全是 zygote64 fork system_server 崩溃(/dev/binder 节点缺失)。magiskd 在跑,但 su 不可用。

排查靠排除法:

  1. 模块逐个全禁 → 能开机
  2. Magisk 版本对比(30.7 / 30.6)→ 都卡
  3. 刷回原厂 vendor_boot → 连续 3 次重启正常(证明 ROM 本身稳定,问题在 Magisk 侧)
  4. 最终定位:chroot-sshd-autostart.sh 在启动早期把 Debian chroot 挂载进 /debug_ramdisk,与 Magisk preinit 机制冲突 → 第二次重启 preinit 挂载异常 → binder 节点缺失 → zygote 起不来

处置:设备 2 降级 Magisk 30.6 + 禁用 chroot 自启脚本,连续多次重启验证通过。

这个事故还顺带挖出一个更隐蔽的地雷:Magisk 的 service.d 按可执行位运行脚本,不看扩展名。文档里写的"禁用 = 改名为 .bak“其实根本没禁——文件还是 -rwxr-xr-x,只是那台恰好 25 小时没重启没暴露。正确做法:文件移出目录、去掉执行位。

远程运维:FRP 双隧道

手机在 CGNAT 后面(4G 公网 IP 一天换 4 次),外网直连无解。方案:手机主动出站连 VPS 上的 frps(就是「我的自建服务清单」里那台),每台开两条隧道:

设备SSHadb(保底通道)
设备 1:6012:6014
设备 2:6008:6009
  • SSH 仅密钥认证,全世界任何地方一条 ssh gold2 进手机
  • adb 隧道是保底通道:即使 Termux 整个被 MIUI 干死,root 层的 adb 依然可进,能修一切。在此之前 Termux 一挂手机就够不着了,只能人肉去插 USB

一个有意思的细节:VPS 的 frps 是 0.51.3(ini 配置),手机 frpc 是 0.71.0(toml 配置),跨 20 个小版本握手正常。但别为此升级 VPS 的 frps——上面挂着 8 条生产隧道(几家店的 SSH、旁路由、远程桌面),升级风险远大于收益。

音乐自动同步链路

手机的主力用途是音乐:/sdcard/Music/音乐U盘 里 682 首 / 6.2GB,Musicolet 离线播。但我想"歌单加歌 → 自动到手机,随时离线听”,于是搭了这条链路:

网易云歌单
   │  systemd timer 每 30 分钟
   ▼
VPS(sync.py:按 song_id 比对线上歌单 ↔ 本地,只下新增)
   │  HTTPS 静态分发 + NATS 通知
   ▼
手机(gold-sync.py --daemon,root 层)
   └ 落盘后触发 MediaStore 重扫 → Musicolet 立即可见

几个设计点:

  • NATS 只报信,文件走 HTTP 拉:NATS 默认 max_payload 1MB,一首歌 8~12MB;VPS 才 1.6G 内存,开 JetStream 不划算。manifest.json 是唯一真相来源,漏收消息不影响最终一致性(还有 30 分钟轮询兜底)
  • 序号对齐重命名(最值的一段逻辑):文件名是 NNN. 歌名 - 歌手.mp3,序号 = 歌单位置。我习惯把新歌加在第一首,加一首 → 后面所有歌序号 +1 → 全部文件名改变。不对齐的话,下载器会重下整个 156 首的歌单(~1.4GB);对齐后是 155 个重命名 + 只下 1 首
  • 冷启动匹配:手机里已有 676 首、VPS 全新下载序号不同,首次同步要用"去掉 NNN. 前缀的文件名"把本地文件认领回 song_id。且一个本地文件只能被认领一次——歌单里有同名同歌手的重复曲目,认领两次就是对同一文件重命名两次直接崩(实际踩过,99 个文件卡在临时名,写了还原逻辑)

最隐蔽的坑:

  • MIUI 会切断非前台应用的网络:Termux 里 curl 全返回 000,连直连 IP 都超时;同一时刻 root 和其他应用全部正常。DNS、WiFi、防火墙全排查了一遍才定位。解法是 termux-wake-lock,开机脚本第一行必须是它
  • 目录名带半角冒号,Android FUSE 拒绝一切写入:歌单目录 Bar 20:30~ 里那个 : 是 FAT 非法字符,Termux 写不进去,连 root 都写不进去(root 继承了 app 的挂载命名空间,su -mm 也无效)。本地目录改成全角冒号 Bar 20:30~,视觉几乎无差别
  • Termux:Boot 靠不住:MIUI 会把 Termux 打成 stopped=true,Android 不向 stopped 应用投递 BOOT_COMPLETED,Termux:Boot 开机永远不触发。最终把所有守护进程迁到 Magisk service.d(root 层)——开机必跑、永远有网、不受任何应用管理影响

实测端到端:22:07 歌单加歌 → 22:17 VPS 下好 → 22:18 手机下好,全程 11 分钟

最终结果

设备 1设备 2
ROMOS3.0.9.0 / _bOS3.0.10.0 / _a
RootMagisk 30.7Magisk 30.6(30.7 在 3.0.10 上卡开机,刻意降级)
可用内存~5.1GB~5.1GB
曲库700 首 / 6.2GB700 首 / 6.2GB
远程SSH + adb 双隧道(公网)SSH + adb 双隧道(公网)
限充80% 停充80% 停充
SIM联通 5G SA(数据关闭)

几个数字:

  • 裁剪后内存可用 78MB → 5.1GB
  • 音乐自动同步端到端 11 分钟
  • 5G 实测(设备 2):下行 2433Mbps,偏低(联通 5G 正常 100300Mbps,套餐限速或信号,待查)
  • root 层看门狗统一巡检 sshd / frpc / 同步守护 / 电池限充,每 60 秒,谁挂了谁拉起

另外写了一个 health-check.sh 例行体检脚本,一条命令出完整报告:身份、服务进程数、网络验证状态、全局代理、电池限充、内存、曲库与 MediaStore 对账、修剪漂移。其中"全局代理"一项是后来加的教训:设备 1 残留过一条 http_proxy 127.0.0.1:8080所有应用断网但 root 完全正常,WiFi 设置界面还显示"已连接",UI 上根本查不到——这种不对称是排查时最大的误导。

最近还给 Termux 装了 musicfox(网易云音乐终端版),配了 Termux:Widget 桌面快捷方式,不用开 app 直接敲命令听歌。后续打算在它们身上试 NAS(OTG 外接存储)和下载节点。

几点收获

  1. 先备份一切。那两份 ~536MB 的 NV 分区备份是这个项目的保命资产——没有它们,解锁那一步根本不敢做。
  2. root 层比应用层可靠。所有"开机必须跑"的守护进程放应用层(Termux:Boot)全被 MIUI 干掉,迁到 Magisk service.d 之后零失效。
  3. “锁文件 + PID” 的单实例写法是重灾区。两个看门狗脚本都因为 PID 复用死锁静默停摆过一天以上(进程死了,PID 被别的进程回收占用,存活检查永远通过)。锁检查必须配合 /proc/<pid>/cmdline 核对身份。
  4. 不要在 adb/ssh 里堆嵌套引号。排查系统状态一律写成脚本文件推上去执行——su -c '...$(cat ...)...' 这种嵌套我把 wakelock 丢失、曲库清零误判了四次,实际都好好的。
  5. 自建的乐趣在这:每个零件都看得见摸得着,坏了修得快,修完还懂了。

本文由 Qwen3.8-27B(OpenRouter)根据两台设备的接手包文档与会话记录整理撰写,我负责审核与修改。