• 首页
  • 关于

鸽子窝:Pigeon's Blog

笨蛋鸽子的小窝
默认分类,技术

在Termux搭建AstrBot+Snowluma,解决component loading failed

2026-09-30 浏览量 0 暂无评论

刷B站,发现SL官方写了Andriod部署教程,这不赶紧把旧手机翻出来试试。
结果部署过程中卡在了神笔问题上——SnowLuma一直报 component loading failed [COMPONENT_LOAD_FAILED]。

环境

项目版本 / 配置
设备小米 8 SE(骁龙 710,8 核,6GB 内存),Android 10,已 root
宿主Termux + proot-distro(Ubuntu)
QQLinux arm64 版 3.2.32
SnowLumav1.14.20 linux-arm64-lite
AstrBotv4.28.2

整体结构大概是这样:

Termux
└── Ubuntu rootfs(proot-distro 装的)
    ├── Xvfb :1 + fluxbox + x11vnc + noVNC
    ├── QQ(Electron)
    ├── SnowLuma(node)── ptrace 注入 ──> QQ
    │     └── OneBot 反向 WS
    └── AstrBot(Python)  

问题:COMPONENT_LOAD_FAILED

一开始完全照常规思路来:proot-distro login ubuntu,装好 QQ 的 deb 和 SnowLuma,写个启动脚本拉起 Xvfb、VNC、SnowLuma、QQ。QQ 能正常启动,SnowLuma WebUI 也能打开,但日志里一直是:

[Hook] auto-load skip: PID=22731 is an Electron child process (--type=zygote)
[Hook] load failed: PID=22729 err=component loading failed [COMPONENT_LOAD_FAILED]

更早之前用 v1.9.12 试过一次,错误更直接:

[Hook] load failed: PID=12060 err=PTRACE_ATTACH failed for trampoline TID 18566: No such process

网上搜到的建议是给 node 加 ptrace 能力:

setcap cap_sys_ptrace=ep "$(readlink -f "$(command -v node)")"

没用。

排查

1. SnowLuma 是怎么注入的

对原生模块跑一下 strings:

strings native/snowluma-linux-arm64.node | grep -iE "ptrace|dlopen|COMPONENT"

能看到 PTRACE_GETREGSET failed、PTRACE_POKEDATA failed、remote dlopen failed for dependency、component loading failed [COMPONENT_LOAD_FAILED] 这些字符串。也就是说:SnowLuma 用 ptrace 附加到 QQ 进程,改寄存器,让 QQ 自己去 dlopen 注入组件 snowluma-linux-arm64.so。

2. 排除组件本身的问题

在容器里直接 dlopen snowluma的so:

ldd snowluma-linux-arm64.so          # 依赖齐全,glibc 2.43
python3 -c 'import ctypes; ctypes.CDLL("/opt/snowluma/app/native/snowluma-linux-arm64.so"); print("dlopen ok")'

能正常加载,说明不是缺库或者架构不对。

QQ 被 proot 追踪

$ grep TracerPid /proc/<QQ的PID>/status
TracerPid:    22654

22654 是谁?就是 proot 进程本身。

这就是根因:proot 的工作原理就是用 ptrace 追踪容器里的每一个进程,拦截系统调用来做路径转换、伪装 root 等。而 Linux 规定一个进程同一时间只能有一个 tracer。SnowLuma 在容器里发出的 ptrace 调用,实际上是被 proot "模拟"的——proot 有一套 ptrace 模拟,能让 gdb/strace 之类勉强在容器里工作,但这种跨进程改寄存器、远程 dlopen 的操作就超出它的能力了。旧版卡在 attach,新版卡在组件加载,本质是同一个问题。

setcap 当然也没用——proot 下根本拿不到真正的内核能力。

因此,在 proot 里,ptrace 注入类的方案基本走不通。 要么换成 chroot,要么换不依赖 ptrace 的框架(比如 NapCat)。

解决:root + chroot,复用 proot-distro 的 rootfs

诶嘿嘿,之前其实被GPT带沟里了,我的手机有 root,那就简单了:proot-distro 装好的 Ubuntu rootfs 就在 $PREFIX/var/lib/proot-distro/containers/ubuntu/rootfs,完全可以直接 chroot 进去,之前装好的 QQ、SnowLuma、AstrBot 都不用重装。chroot 下没有 proot 占着 tracer,SnowLuma 的 ptrace 就是真正的内核 ptrace。

需要自己做的是 proot 原来帮你做的那些挂载:

R=/data/data/com.termux/files/usr/var/lib/proot-distro/containers/ubuntu/rootfs
mount -t proc proc   $R/proc
mount -t sysfs sysfs $R/sys
mount -o bind /dev      $R/dev
mount -o bind /dev/pts  $R/dev/pts
mkdir -p $R/dev/shm
mount -t tmpfs -o mode=1777 tmpfs $R/dev/shm      # Chromium/Electron 需要

换成 chroot 启动之后,SnowLuma成功注入了!

从 proot 换到 chroot 的坑

proot 帮你默默做了很多事,换成 chroot 以后这些都得自己补上。

坑 1:x11vnc 起不来,noVNC 报"无法连接到服务器"

x11vnc 日志最后一行:

shmget(scanline) failed.
shmget: Function not implemented

Android 内核没有 System V IPC(shmget 这一套)。proot 启动参数里的 --sysvipc 就是在模拟它,chroot 下就没了。解决办法是给 x11vnc 加 -noshm。

坑 2:/dev/shm

Android 的 /dev 下没有 shm,Chromium/Electron 又重度依赖它。bind 完 /dev 之后要单独挂一个 tmpfs 到 $R/dev/shm。

坑 3:以 root 跑会把文件属主搞乱

chroot 里默认是真 root。QQ 和 SnowLuma 以 root 跑问题不大,但 AstrBot 的代码和数据在 Termux 的 home 里,以 root 跑会生成一堆 root 属主的文件,以后回到普通用户就没权限改了。所以 AstrBot 用 setpriv 降权回 Termux 的 uid。

坑 4:降权后连不上网

Android 有个 "paranoid network" 机制:进程必须在 inet 组(gid 3003)里才能创建网络 socket。setpriv 降权时要带上 --groups=3003,否则 AstrBot 一个请求都发不出去。

坑 5:普通用户看不到 root 进程

Android 的 /proc 挂了 hidepid,Termux 普通用户下 ps 看不到 root 跑的 QQ 和 SnowLuma。一度以为进程挂了,其实要 su -c ps 才能看到。

坑 6:Termux 的 uv 进不了 chroot

AstrBot 用 uv 管理依赖,但我的 uv 是 Termux 装的 Android 原生(bionic)二进制,proot 里之所以能用是因为 proot bind 了 Termux 的 $PREFIX。chroot 里没有 Android 的链接器环境,跑不起来。好在装依赖是一次性操作、不涉及注入,所以装依赖还是用 proot,运行用 chroot:

proot-distro login ubuntu --termux-home -- bash -lc \
  'cd ~/AstrBot && UV_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple uv pip install --python .venv/bin/python -r pyproject.toml'

安全

  • SnowLuma WebUI 只监听 127.0.0.1。 ,安全起见建议用 SSH 隧道访问:

    ssh -N -L 5099:127.0.0.1:5099 -L 6081:127.0.0.1:6081 termux
  • x11vnc 用了 -nopw,所以一定要 -localhost,noVNC 也只听 127.0.0.1。
  • 一个小惊喜:只重启 SnowLuma 的 node 进程不需要重启 QQ,它会检测到 QQ 里已经有注入的组件并直接重连(already has SnowLuma pipe; will reconnect),登录状态不丢。

保活

老米杀后台很凶,这个比放本地还是云上影响更大:

  • Termux 里 termux-wake-lock;
  • MIUI 设置里给 Termux 关掉省电优化、打开自启动;
  • 最近任务里把 Termux 锁住。

总结

  • proot 下 ptrace 注入走不通,这是原理限制,不是配置问题。看到注入失败先查一下 TracerPid。
  • 有 root 的话,直接 chroot 进 proot-distro 的 rootfs 是成本最低的方案,已经装好的东西全部复用。
  • 换 chroot 以后要补上 proot 原来代劳的东西:SysV IPC(-noshm)、/dev/shm、Android 的 inet 组、文件属主。

一台 2018 年的手机,现在 7×24 小时跑着 QQ + OneBot + LLM 机器人,挺好。

XJTU校园网 IPv6 + OpenWrt 2026ver
没有了

评论/ 取消评论

  • 文章目录
  • 站点概览
    author

    16 日志
    6 分类
    Creative Commons

    浏览量 : 0

    © 2026 鸽子窝:Pigeon's Blog. Power By Typecho . Theme by Shiyi

    This is just a placeholder img.