1 前言

在上一篇文章《把 glibc 移植到鸿蒙 PC 上:它能用来做什么》中,我展示了将 glibc 移植到鸿蒙 PC 上的可行性,这让一批依赖 glibc 的 Linux 程序能直接在鸿蒙 PC 上跑起来。

移植完之后,我想做个更激进的验证:既然 glibc 程序能跑,那发行版的包管理器呢?能不能不止跑单个程序,而是直接 apt install Debian 官方仓库里的软件包?

基于这个目标,我做出了 PreLinux 这个开源项目。它能把一整个发行版装进你指定的前缀目录,用发行版自己的包管理器安装软件,不需要 root,不需要虚拟机,也不需要容器。

项目地址:https://github.com/hqzing/prelinux

2 先看效果

在 HiShell 中操作,几步就能完成安装:

# 下载安装脚本
curl -fsSLO https://github.com/hqzing/prelinux/releases/latest/download/install.sh

# 执行安装脚本
# 默认安装 debian:12 到 ~/prelinux,脚本从 GitHub 下载文件,注意网络连通性
sh install.sh

# 进入实例
source ~/prelinux/debian12/env.sh

# 用 apt 装软件
apt update
apt install -y tree
tree -L 1 /

tree -L 1 / 输出的就是实例自己的根目录:

/
|-- bin -> usr/bin
|-- boot
|-- dev
|-- etc
...
`-- var

19 directories, 0 files

这个 / 不是宿主机的根目录,而是 ~/prelinux/debian12。apt、dpkg 的数据库,/etc/apt、/var/lib/dpkg、/usr 全部在这个目录里面,所以 apt 能像在真机上一样工作。仓库的 GPG 校验、依赖解析、维护脚本都没有关掉。

source 只影响当前这个 shell。想回到宿主的普通环境,可以开一个新终端。

所有内容都在 ~/prelinux 下,卸载时删掉这个目录即可。

如果需要指定前缀,可通过 PRELINUX_HOME 变量进行配置:

PRELINUX_HOME=/opt/prelinux sh install.sh

在实例内换源,操作与普通的 Linux 环境无异:

sed -i 's|http://deb.debian.org/debian|http://mirrors.aliyun.com/debian|;
        s|http://deb.debian.org/debian-security|http://mirrors.aliyun.com/debian-security|' \
    /etc/apt/sources.list.d/debian.sources
apt update

apt 在使用过程中会打印一条 Setting TIOCSCTTY ... failed,这是鸿蒙内核拒绝了那个 ioctl,apt 会继续执行,忽略即可。

3 它不是虚拟机,也不是容器

把它和几种常见方案放在一起比较,差别主要在这几个地方:要不要自带内核、要不要 root、和宿主系统是什么关系。

方案需要一个内核需要 root和宿主的关系
虚拟机是否完全隔离
容器复用宿主内核通常需要靠 namespace 隔离
proot复用宿主内核否靠 ptrace 拦截系统调用
PreLinux复用宿主内核否靠改过的 glibc 改写路径

原理上就一句话:实例里的程序跑在一份改过的 glibc 上,这份 glibc 把 FHS 路径重定向进实例目录。

  • /bin、/lib、/usr、/etc、/var 指向 ~/prelinux/<发行版> 里的对应目录;
  • /proc、/sys、/dev 直通给宿主;
  • 非 FHS 路径(比如鸿蒙的 /storage、/system)原样直通。

包管理器把实例目录当成自己的 /,所以 apt 这类普通 Linux 程序不需要任何改造就能工作。

还有一个绕不过去的环节是签名。鸿蒙 PC 强制校验可执行文件和 .so 的代码签名,未签名的二进制无法运行,而软件包里的二进制原本都是给发行版用的,既没有签名,解释器路径也不指向实例。PreLinux 的做法是让包管理器在解包时自动处理:dpkg 的落盘路径上挂了钩子,每装完一个可执行文件就改写解释器路径并立即签名。所以 apt install 装完的软件可以直接运行,用户不需要再手动签一次。

4 装几个常用的软件

PreLinux 不只能跑 tree 这种小工具,下面这几个稍微复杂一点的软件也能正常工作:

apt update
apt install -y curl python3

# git 会推荐 openssh-client,它要创建系统用户,实例里这一步会失败,因此跳过它
apt install -y --no-install-recommends git

# 装完直接运行
curl -sI https://www.baidu.com | head -3
python3 -c "import sys; print(sys.version)"
git clone --depth 1 https://github.com/git/git

curl 能正常发起 HTTPS 请求,说明 OpenSSL、CA 证书、DNS 解析这条链在实例里都是通的;python3 能启动,说明解释器和它的整套共享库也正常;git 能从 GitHub 克隆一个开源仓库,说明它依赖的 libcurl、perl 以及证书链也都完整可用。

请添加图片描述

请添加图片描述

5 目前的限制

这套方案的边界由它的实现方式决定,以下是一些主要的限制:

  • 静态链接的程序不走映射。Go 程序、部分 musl 静态工具直接访问宿主真实路径,看到的是宿主的文件系统。
  • 实例内不能编译。任何语言都不行:动态 ELF 要 relocate 才能在本实例运行,静态 ELF 又不过映射。软件来源是发行版预编译包,pip 只能装带 wheel 的包。
  • 不能升级 glibc。实例里的 glibc 是改过的,升级会破坏路径映射,所以 apt 会跳过 libc6。
  • 实例不能移动或改名。实例根写死进了 glibc 和每个可执行文件的解释器路径,需要另一个位置就重新导入一个实例。
  • 需要特权的操作不可用。mount、namespace、cgroup、setuid、加载内核模块都不行,没有图形界面。
  • 要创建系统用户或组的软件包可能装不上。内核拒绝 audit netlink 连接,groupadd、useradd 会失败并让 postinst 返回错误,可以加 --no-install-recommends 跳过这类推荐依赖。
  • 权限和属主信息不可信。hmdfs 忽略 chmod 也没有扩展属性,ls -l 显示的模式和实际行为不一致;文件内容、大小、时间戳是正常的。

6 结语

PreLinux 和 Harmonybrew 解决的不是同一个问题。

Harmonybrew 把软件包原生适配到鸿蒙(musl 工具链、原生构建),面向日常使用。PreLinux 则是一次边界测试,探索鸿蒙对 Linux 二进制生态的兼容能做到多深。

就如上文所说,它存在诸多限制,并不适合日常开发。第 5 节那些限制里,随便一条都会在长期使用中变成麻烦:不能编译、不能升级 glibc、实例不能移动、权限元数据不可信。

如果你是急需使用发行版仓库中的某个软件,可以把它当作一个临时的、可用的手段。如果你想要一套稳定的开发环境,Harmonybrew 才是当前最佳的选择。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐