本篇文章,聊一聊尝试让安卓手机原生运行 Ubuntu,尤其是运行官方未发布过的 ARM 架构的 Ubuntu 24.04 桌面版本。
最近的几篇文章,都包含了比较多的实操内容、需要反复的复现验证,以及大量的调试过程,为了不把内容沉底草稿箱,就先分章节发布出来啦。
之所以会有这篇文章,是因为写完最近几篇文章,后台收到了不少读者留言和私信,其中有几条是关于手机运行 Linux 操作系统,以及 Docker 性能问题的沟通。
在回复中,我提到了会在折腾恢复 Android 裁剪前的、适合 Docker 运行的内核环境,构建系统来验证为什么《Docker 加持的 安卓手机:随身携带的知识库(一)[1]》这篇内容里,容器执行效率非常慢的问题。
但是,作为一个懒人,总归在想,有没有什么更简单的、更可持续的维护方案:
•毕竟,每当安卓版本升级(包括 Linux 内核升级),如果我们想使用最新的系统,总归要重新构建和验证。•以及,在 Android 环境中,即使能够将 Docker 作为独立的可靠应用运行,但其实有很多程序,并不需要在容器中运行,如果本身就是 Linux 环境,那么折腾的成本不就更低了嘛。
况且,如果验证成功,之前写过的几十篇 Ubuntu[2]、Docker 相关的文章[3]不就都能有更轻量的验证环境了嘛。
不过,虽然目前 Ubuntu 官方推出了适用于 ARM 服务器的系统镜像[4]。但是,官方压根没推出过桌面版本的 ARM 系统镜像。
想要得到 ARM 架构的 Ubuntu 24.04 桌面版的操作系统看起来只能由我们自己构建啦。(吗?)
作为一个懒人,能不动手就坚决不动手。 还记得更早些的一篇文章里[5],我在搭载了 M2 的 MacBook Pro 设备上安装了 24.04 版本的 Ubuntu 吗?这个操作系统就是桌面版本的。如果我们能够得到这个操作系统,那么可能能省不少功夫。
说干就干,至于跑 Windows 打游戏什么的,等干完正事再说吧。
不一样的 Ubuntu 系统历史:Ubuntu Phone 和 Ubuntu Touch
看完上面的内容,我相信有一些“互联网阅历”的朋友,一定会想起 “Ubuntu 移动操作系统”。但之所以不适配这个“更适合手机”的系统版本,而是选择适配标准版、更新的桌面版本,或许了解完它的故事,你会和我做出一样的选择。
曾经的 Ubuntu Touch
在 2011 年,Ubuntu 宣布发布了适用于移动端、平板、电视等设备的分支操作系统:Ubuntu Touch[6]。但是在 2017 年的时候,由于市场反馈不佳,Canonical 宣布停止维护该项目,随后改项目由 2015 年发起的 UBports 项目所在的基金会收购,原本官方支持的项目转变为了社区产品。
目前来看,这个系统支持的设备非常有限[7],参考资料也并不多,所以就不考虑从这个角度做“Linux 适配”啦。
曾经的 Ubuntu Phone
另外一个有些关联,但是目前看来也步入历史尘埃的项目 Ubuntu Phone[8],将 Ubuntu 以 Android 系统方式融入手机,并提供使用 “QT、JS、HTML” 定义手机 App 的能力[9],也因为公司商业化方向调整,而被放弃,时间在 2014 年,比上面的 Touch 项目还更早一些。
自此以后,Ubuntu 官方就基本一心扑在“服务端”、“云市场”、“AI 市场”、“部分消费者日常笔电设备”上了。
回到《MacBook Pro 原生安装 Ubuntu 24.04 ARM 版[10]》这篇文章里,我们使用的是 Asahi 项目的衍生项目 UbuntuAsahi/ubuntu-asahi[11],翻阅项目文档,我们不难得到项目的安装方法:
如果我们将命令末尾的 删除,再次执行命令,就能够一窥安装脚本的庐山真面目啦:
安装程序比较简单,基本就是定义一些“资源”,然后分别下载“真正的安装程序()”和“安装程序数据()”,然后调用安装程序,完成安装。
让我们使用 来看看安装程序数据文件里都有些什么吧:
在 中定义的内容,看起来就是我们要找的预构建好的 ARM 架构的系统镜像文件,虽然是 23.10 版本的 Ubuntu,但是我们可以和之前那篇文章一样,尝试升级系统到最新版本嘛。
不过,不论我们怎么和前面的安装程序中,预先定义好的路径进行拼合,就是无法下载到这个 镜像压缩包。看来,需要研究下 Installer 这个隐藏在背后的真正的安装程序啦。
不论是选择使用 模拟上面的逻辑,下载程序到本地,还是直接访问安装程序本体的开源项目 AsahiLinux/asahi-installer[12],我们都不难定位到位于 src/osinstall.py[13] 的核心函数 :
原来在下载镜像的时候,程序会额外地做一个拼合动作,在地址中加一个 目录。依样画葫芦,拼合上面得到的信息,得到能够下载的资源地址:
使用 或者下载工具完成预构建镜像的下载后,我们使用 对其解压缩,来看看里面有哪些东西:
可以看到镜像压缩包中,主要包含了三部分:
•:根据名字来看是主要负责启动的分区镜像,具体内容需要还原分区后验证。•:用于引导这个镜像的 启动分区程序( 的缩写就是 )•:“根分区”镜像,看了下文件大小,足足有 8GB,应该是 Ubuntu 系统本体啦。
上一篇文章中 “对手机进行分区调整[14]” 小节中,我们介绍了如何对手机进行分区划分和处理。不过当时的目标是安装 Windows 系统,和现在的验证 Ubuntu 安装的场景需求不太匹配。
所以,我们来参考上篇文章,并略做调整,折腾出一个适用于上面 Ubuntu 镜像的环境:划分出三个新的分区。
恢复手机分区环境
重启手机并进入 “TWRP” 模式,确保 能够“看到”你的设备:
首先,还是使用 和 命令,补全手机恢复环境中没有的 工具,然后对 磁盘进行分区更新:
和上篇文章提到的一样,我们不要“摸黑”操作,先 确认下环境:
能够看到分区和上一篇文章中的一致,在 目录后,保留着我们新划分的 、、 三个分区。
先来“恢复现场”,手动删除掉这三个分区,使用 + 分区编号即可:
执行完分区的还原后,使用 查看下分区状态:
系统中由恢复了 的空余空间。
划分新分区
参考上篇文章中的操作,我们来重新划分 分区,并设置分区可引导:
设置完毕,使用 确认磁盘状况,以及确保上面的操作已经生效:
接下来,我们分别创建用于存放上文中我们得到的 分区镜像和 分区镜像的分区:
当命令执行完毕,我们会看到类似下面的日志输出:
还是使用 确认磁盘状况,确保上面的操作已经生效:
好了,重启手机,让分区生效。
接下来,我们来尝试将下载的 ARM 架构的镜像还原到上面准备好的手机分区中。
传输文件到手机内部存储
重启手机,等待手机再次进入“TWRP 模式”。
接着,先将刚刚我们下载并解压缩的 Ubuntu 镜像的三个部分传输到手机内的存储。这样可以确保我们在“还原磁盘”的过程中,不会出现数据线断开的麻烦事情。
由于这台手机的 USB 接口是 2.0 协议,传输完毕所有文件,大概需要几分钟。如果你的手机内部存储空间比较有限,你可以先一个一个文件的传输并做还原,在还原后,手动删除掉传完的文件,来节约磁盘空间。
使用 可以看到,大概需要消耗手机上 的磁盘空间:
使用 还原磁盘
当我们的文件上传到手机内部空间后,我们可以使用 来读取手机内部的文件,并写入上文中创建好的分区中,先来恢复 系统分区:
接着,我们来恢复 分区:
恢复 ESP 分区
想要初始化 所在的 分区,我们还需要一些额外的操作。
先使用 对我们创建好的 分区进行格式化:
接着,创建一个用于挂载 分区的路径,并使用 将分区挂载到系统中:
现在,我们就能够将我们下载的数据全部都还原到手机里啦。
虽然,我们知道此时此刻,大概率手机是无法正确引导 Ubuntu 的,但是还是可以重启手机,看看我们距离目标还有多远。
重启设备进行验证
我们在手机上看到了熟悉的 Ubuntu Grub 引导界面,说明 GRUB 相关程序可用。
GRUB 引导报错
但是,当读条结束,紧随其后的是一行小字报错:
搜索这个报错,在 Kernel.org 的一次提交记录中,找到了这个 EFI 程序报错[15]:
继续搜索这个函数的名称,在另外一个 Linux 相关的网站上,能够看到这个函数的定义[16]:
虽然,我们不熟悉这块代码和程序实现,但是结合注释和返回“非0”(正确)逻辑来看,应该是我们启动设备过程中的“设备树”相关的配置或者程序有问题。将排查目标转向 “设备树” 或许会有突破。
所以,到目前为止,我们知道了这个方案或许可行,GRUB 可以运行,但是设备树的配置存在问题、GRUB 的配置可能也有问题。
什么是设备树(Device Tree)
可能有的同学第一次听到这个名词,因为在本文中非常有用,所以我们简单展开下。(你可以阅读这篇课程“OSD335x Lesson 2: Linux Device Tree[17]”,来了解更多)
简要设备树结构图示意(OSD335x Lesson 2)
Linux 设备树是一种用于描述硬件配置的数据结构,它独立于操作系统的代码之外,能够将硬件的描述与操作系统代码分离,使得内核可以独立,和设备不耦合。
主要的作用和特点有:
1.硬件描述:设备树文件(.dts)使用一种可读的文本格式,描述系统中的硬件组件、它们的属性和连接关系。2.可移植性:通过使用设备树,Linux 内核可以在不同的硬件平台上运行,而无需为每个平台定制内核代码。3.动态加载:设备树在系统启动时被加载到内存中,内核可以动态地访问和解析设备树信息。4.设备驱动程序:设备树信息有助于简化设备驱动程序的编写,驱动程序可以通过设备树获取必要的硬件信息。5.二进制格式:设备树源文件需要通过编译工具转换为二进制的设备树 Blob(.dtb),以便内核解析。6.覆盖机制:通过使用设备树覆盖(Device Tree Overlay),可以在运行时动态地修改设备树,而无需重新编译内核。
为了解决上面的问题,我们首先需要看看恢复的手机分区内容都是什么呢。
还原现场,静态观察
让我们再次重启手机,将处于 TWRP 模式的手机和电脑相连,然后执行 进入交互式环境。
为了确保我们排查的文件和上面手机首次运行的一致,这里我们可以再次重复“第四步”中的步骤,来一个“现场恢复”。
然后,依次执行下面的命令,将上文中的几个分区挂载到手机系统中。
接着,我们先来简单查看不同分区中的内容:
分区的内容看起来比较正常,目前我们不能确认,也还不需要确认内容是否有问题,可以先跳过,稍后再分析。
和 分区中的内容,都掌管着设备的启动,尤其是 中的文件,看起来有 设备绑定关系,或许需要详细排查。
因为 中存在比较多的“冗余文件”,通常情况不需要这么多文件,所以我们不妨先看看,是否存在软链的情况:
通过上面的信息,我们可以得到简化上面的文件关系:
如果这时,我们将手机重启,尝试加载操作系统,然后再重复这一步(第五步)的操作,会发现 和 分区的内容会变成一致的。
所以,或许我们可以少分析一些内容了。
获取运行后的配置状态
经过反复验证,手机会在启动的时候,将 目录的内容,完整镜像到 分区。所以,我们可以再多忽略一个分区的内容( 分区),只对 分区内容进行分析。
依旧是进入 交互终端环境,将 分区挂载到手机系统中:
然后,可以使用 将手机上的分区数据都下载到本地,方便后续的分析:
分析下载至本地引导程序的问题
我们根据上文中的信息,将本地的文件和目录去重,将得到类似下面的目录内容:
如果你曾经经常安装系统,能够一眼认出来,这不就是 **兼容 UEFI 和 BIOS **的 使用的程序嘛。
直接定位 中的核心内容,定义启动 Ubuntu 的配置部分:
排除掉各种常规设置配置外,下面这两条指令最可能是不能正常运行的 “罪魁祸首”(关于 GRUB 的资料,感兴趣可以阅读 ArchLinux WIKI 的相关文档[18]或者 GNU 的 GRUB 文档[19]):
在根目录中的 配置文件中,我们能够看到生成 和 使用的配置,或许,我们可以试试用符合这台手机的 Linux 内核和设备树文件,来生成符合引导程序使用的内容。
说到构建可用的引导内容,Intel 有一个很好的参考资料:“构建 UEFI 引导加载程序[20]”,如果你感兴趣,也可以了解。
我们简化构建引导程序内容,尤其是生成包含生成 和 产物内容,一般会有以下几个步骤:
1.准备编译环境,以及准备某个指定版本内核的 Linux 源码。2.定制内核功能,使用 生成我们需要的构建配置。3.编译内核源码,得到 ,即:。(参考:为什么 vmlinuz 和 bzImage 是相同的[21])4.制作 携带初始化启动脚本的微型根文件系统。5.打包文件,更新 配置。
虽然上面的制作引导系统的基本流程看起来非常简单,但是实际操作中还是有一些细节需要注意:
1.内核配置项极多;2. 中要包含必要的设备驱动和工具,需要寻找补全,并需要控制 产物的尺寸不要过大(加载不起来);3.胆大信息,反复调试。
至于编译需要的算力,在 soulteary/Home-Network-Note[22] 项目中,列举了我目前的设备状况,即使前几篇文章中提到的能够当高性能 CI/CD 的 ARM Ubuntu 的设备不能完成构建,也有一些其他的可选项(x86),问题不大。
五一假期最后一天,突然想到了这个方法,至于能否复活这个特别的 “Ubuntu Phone”,让它丝滑的运行 Ubuntu 24.04 和 Docker 程序,接下来相关的文章里,我们来一起试试看吧。
--EOF
引用链接
Docker 加持的 安卓手机:随身携带的知识库(一): https://soulteary.com/2024/05/03/docker-powered-android-phone-knowledge-base-you-can-carry-with-you-1.html 几十篇 Ubuntu: https://soulteary.com/tags/ubuntu.html Docker 相关的文章: https://soulteary.com/tags/docker.html 适用于 ARM 服务器的系统镜像: https://ubuntu.com/download/server/arm 更早些的一篇文章里: https://soulteary.com/2024/05/02/macbook-pro-natively-installs-arm-ubuntu-24-04.html Ubuntu Touch: https://ubuntu-touch.io/zh_CN/ 这个系统支持的设备非常有限: https://devices.ubuntu-touch.io/device/mimameid/ Ubuntu Phone: https://phone.docs.ubuntu.com/en/ 使用 “QT、JS、HTML” 定义手机 App 的能力: https://phone.docs.ubuntu.com/en/apps/qml/ MacBook Pro 原生安装 Ubuntu 24.04 ARM 版: https://soulteary.com/2024/05/02/macbook-pro-natively-installs-arm-ubuntu-24-04.html UbuntuAsahi/ubuntu-asahi: https://github.com/UbuntuAsahi/ubuntu-asahi AsahiLinux/asahi-installer: https://github.com/AsahiLinux/asahi-installer src/osinstall.py: https://github.com/AsahiLinux/asahi-installer/blob/main/src/osinstall.py 对手机进行分区调整: https://soulteary.com/2024/05/04/android-phones-running-windows-1.html#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E5%AF%B9%E6%89%8B%E6%9C%BA%E8%BF%9B%E8%A1%8C%E5%88%86%E5%8C%BA%E8%B0%83%E6%95%B4 这个 EFI 程序报错: https://lore.kernel.org/linux-efi/20200430182843.2510180-4-nivedita@alum.mit.edu/ 函数的定义: https://elixir.bootlin.com/linux/latest/source/scripts/dtc/libfdt/fdt.c#L89 OSD335x Lesson 2: Linux Device Tree: https://octavosystems.com/app_notes/osd335x-design-tutorial/osd335x-lesson-2-minimal-linux-boot/linux-device-tree/ ArchLinux WIKI 的相关文档: https://wiki.archlinux.org/title/GRUB GNU 的 GRUB 文档: https://www.gnu.org/software/grub/manual/grub/html_node/Simple-configuration.html 构建 UEFI 引导加载程序: https://www.intel.com/content/www/us/en/docs/programmable/683134/current/building-the-uefi-boot-loader.html 为什么 vmlinuz 和 bzImage 是相同的: https://unix.stackexchange.com/questions/197225/is-vmlinuz-and-bzimage-really-the-same soulteary/Home-Network-Note: https://github.com/soulteary/Home-Network-Note
如果你觉得内容还算实用,欢迎点赞分享给你的朋友,在此谢过。