我最近在开发 GoblinReactor,一个 Android 终端和本地开发环境。它在普通 Android 应用内运行 Linux® 内核和 Debian ARM64 软件包。你可以用 apt 安装工具、编辑文件、打开多个终端,还可以运行自己的服务。所有程序都在本地运行;安装时不需要 Android 的 root 权限,也不需要远程服务器。

这是实验性软件。 我已经准备好了 0.3.8 预览版,加入了 Kitty 图形支持。请把它用于实验,并在应用之外备份你在意的东西。

0.3.8 Android 预览版已经可以下载。Google Play 注册还在等 D-U-N-S 编号,所以目前需要直接下载安装 APK。办手续也有自己的依赖图。

留在手机里的开发环境

实用之处在于,你有了一个可以保留工作成果的地方。装个编辑器,写个脚本,把文件留在主目录里,之后再回来继续。多个终端共享同一个环境。接上实体键盘会方便些,屏幕上也提供了 Esc、Ctrl、Alt、Tab 和方向键。终端使用 kitty 的原生渲染器。

默认账户是 goblin,主目录为 /home/goblin,在客户机内可以免密使用 sudo。软件包、账户和文件都保存在 Android 应用私有存储中的 ext4 磁盘镜像里。正常安装签名一致的预览版更新会保留这块磁盘。

Android 模拟器中的 GoblinReactor,屏幕键盘上方显示 7.2.9-goblin 内核、goblin 账户和已安装的命令行软件包。
Android 16 模拟器上的 GoblinReactor 0.3.7。命令行解释器和软件包都在本地运行。点击图片可查看原始尺寸的截图。

可以先做个小实验:

sudo apt update
sudo apt install -y hello
hello

往手机里塞了一个操作系统之后,再装一个唯一任务就是说声 hello 的程序,还挺让人满足的。

终端里的图形

GoblinReactor 0.3.8 可以显示 Kitty 图形。图像可以直接随终端数据传输,也可以来自客户机中的文件、临时文件或 POSIX 共享内存。文件访问使用前台进程在客户机中的权限。即使使用共享内存,图像数据也仍需跨越客户机与 Android 之间的边界进行复制。

这些截图来自 Android 16 模拟器,展示了 0.3.8 中的 Kitty 图形支持。 两张截图都保留了屏幕键盘。

先看 Goblin Purrfect 在 GoblinReactor 中编辑一元二次方程的求根公式。公式编辑器自动选择了 Kitty shared-memory PNG。修改公式下方的源码,渲染后的预览也会随之更新。

Android 模拟器中,Goblin Purrfect 在 GoblinReactor 内运行,显示平滑渲染的一元二次方程求根公式、可编辑源码、Kitty shared-memory PNG 状态和屏幕键盘。
在 GoblinReactor 内运行的 Purrfect 公式编辑器。公式通过 Kitty 图形协议显示。点击图片可查看原始尺寸的截图。

接下来是 gnuplot 6.0.2,通过 Debian 的 gnuplot-nox 软件包(6.0.2+dfsg1-1)安装。这个构建包含 kittycairo,使用 Cairo 和 Pango 绘图,再通过 Kitty 图形协议将结果发送到终端。这里绘制的是 sin(r)/r,其中 r = sqrt(x*x + y*y),原点处的值为 1。

Android 模拟器中,gnuplot 6.0.2 在 GoblinReactor 内运行,通过 kittycairo 显示彩色三维波纹曲面,下方保留屏幕键盘。
Debian 的 gnuplot 通过 kittycairo 直接在终端中绘图。曲面图、命令行解释器和 Android 键盘共用一个屏幕。

要在 GoblinReactor 0.3.8 中重现这张图,先将示例脚本保存为 kitty-ripple.gp,再运行:

sudo apt install --no-install-recommends gnuplot-nox fonts-dejavu-core
gnuplot --version
gnuplot -e 'set terminal kittycairo; show terminal'
gnuplot kitty-ripple.gp

脚本选择 kittycairo,设置图像尺寸,然后绘制曲面。这是普通的 Debian 软件包在使用终端的图形支持。

在用户空间运行 Linux 内核

GoblinReactor 使用 User-Mode Linux,简称 UML:将 Linux 内核编译成用户空间程序来运行。预览版基于 Linux 7.2.9,使用 zalexdev 的 ARM64 UML 移植,再分别应用 Android 集成和并行执行补丁。这个 ARM64 移植是本项目不可缺少的一部分。

客户机的 ARM64 指令在宿主进程中原生执行。UML 的 seccomp 后端拦截客户机的系统调用,交给随应用提供的内核处理。进程管理、内存语义、权限、文件系统、套接字和伪终端都由 Linux 实现。在客户机内,systemd 以 PID 1 运行,负责管理服务。

这样,普通软件包就有了一个可以调用的 Linux 内核。不过,系统调用、调度以及客户机与 Android 之间的切换确实有开销。单凭指令原生执行这一点,还无法判断某项具体任务的性能。

在 Android 一侧,内核运行在隔离服务中,网络套接字由另一个服务处理。Android 的权限和资源管理仍然有效。在客户机内取得 root 权限,可以控制客户机;手机仍由 Android 管理。

源码仓库里有架构和构建细节。下载页还提供了与预览版对应的完整源码归档、构建说明、各组件的许可声明和校验和。

终端关了,服务还可以继续运行

关闭终端会结束其中的命令行解释器。脱离终端的服务可以在所有终端都关闭后继续运行。要正常关闭整个客户机,请使用 Shut down environment…(关闭环境)。

Keep environment awake(保持环境唤醒) 控制用于熄屏工作的唤醒锁。这会耗电。Android 仍可能因内存压力、强行停止操作或后台管理策略而终止应用。突然停止就像机器断电:已经写入磁盘的文件和正在运行的进程,能保留到什么时候是两回事。

另有一个可选的 Start environment after reboot(设备重启后启动环境) 设置。启用后,设备重启并首次解锁时会启动环境,让已启用的服务重新启动。重启前的进程不会因此复活。

存储也需要留意。客户机使用稀疏磁盘,与手机上的其他内容共享物理存储。客户机显示的剩余空间,并不保证 Android 真有这么多空间可用。Back up environment…(备份环境) 通过 Android 文件选择器导出备份。请把备份保存在应用之外:卸载 GoblinReactor 或清除其应用数据,都会删除本地环境。

这里的实验性意味着什么

目标运行环境是 Android 12 或更新版本,并且 Android 系统本身必须是 64 位 ARM64 系统。只有 64 位处理器、却运行 32 位 Android 系统的设备不符合要求。Android 10 和 11 的兼容性仍处于实验阶段,验证有限。

最近的版本检查覆盖了 Android 16 模拟器,其中包括使用 16 KiB 内存页的宿主系统。0.3.6 的运行检查涉及软件包安装、网络、多个终端、备份与恢复、更新后数据保留以及并发任务。0.3.7 沿用相同的内核和客户机资源,检查重点是更新后的应用部分。更早的版本还在一台运行 Android 16 的三星手机和 Android 12 模拟器上运行过。0.3.7 的验证说明列出了该版本的验证范围。

更多实体设备、Android 13–15,以及依靠电池长时间运行的情况,都还需要测试。我预计会有缺陷,包括 Android 应用生命周期和会话中断后恢复方面的问题。现阶段,可以在这里尝试开发工作,但重要内容要另留一份。

试用预览版

在手机上下载 GoblinReactor,打开 APK;如果 Android 提示,请允许你使用的浏览器或文件管理器安装应用。不需要开发者模式或 ADB。首次启动会使用随应用提供的资源在本地准备环境,可能需要几分钟。安装额外的软件包需要网络连接。

有用的反馈要具体:手机型号、Android 版本、尝试了什么,以及实际发生了什么。首次启动、键盘输入、你平时用的编辑器、熄屏后的运行情况,还有能否恢复备份,都是不错的测试起点。应用中的 Save diagnostics…(保存诊断信息) 命令会生成本地报告,你可以先查看内容,再决定是否随缺陷报告一起提交。


Linux® 是 Linus Torvalds 在美国及其他国家的注册商标。

GoblinReactor 独立于 Debian 项目,未获该项目赞助或认可。Debian 是 Software in the Public Interest, Inc. 拥有的注册商标。