当前位置: 首页 > news >正文

安卓手机原生运行 ARM Ubuntu 24.04 桌面版(一)

本篇文章,聊一聊尝试让安卓手机原生运行 Ubuntu,尤其是运行官方未发布过的 ARM 架构的 Ubuntu 24.04 桌面版本。

写在前面

最近的几篇文章,都包含了比较多的实操内容、需要反复的复现验证,以及大量的调试过程,为了不把内容沉底草稿箱,就先分章节发布出来啦。

之所以会有这篇文章,是因为写完最近几篇文章,后台收到了不少读者留言和私信,其中有几条是关于手机运行 Linux 操作系统,以及 Docker 性能问题的沟通。

在回复中,我提到了会在折腾恢复 Android 裁剪前的、适合 Docker 运行的内核环境,构建系统来验证为什么《Docker 加持的 安卓手机:随身携带的知识库(一)》这篇内容里,容器执行效率非常慢的问题。

但是,作为一个懒人,总归在想,有没有什么更简单的、更可持续的维护方案:

  • 毕竟,每当安卓版本升级(包括 Linux 内核升级),如果我们想使用最新的系统,总归要重新构建和验证。
  • 以及,在 Android 环境中,即使能够将 Docker 作为独立的可靠应用运行,但其实有很多程序,并不需要在容器中运行,如果本身就是 Linux 环境,那么折腾的成本不就更低了嘛。

况且,如果验证成功,之前写过的几十篇 Ubuntu、Docker 相关的文章不就都能有更轻量的验证环境了嘛。

不过,虽然目前 Ubuntu 官方推出了适用于 ARM 服务器的系统镜像。但是,官方压根没推出过桌面版本的 ARM 系统镜像。

想要得到 ARM 架构的 Ubuntu 24.04 桌面版的操作系统看起来只能由我们自己构建啦。(吗?)

作为一个懒人,能不动手就坚决不动手。 还记得更早些的一篇文章里,我在搭载了 M2 的 MacBook Pro 设备上安装了 24.04 版本的 Ubuntu 吗?这个操作系统就是桌面版本的。如果我们能够得到这个操作系统,那么可能能省不少功夫。

说干就干,至于跑 Windows 打游戏什么的,等干完正事再说吧。

不一样的 Ubuntu 系统历史:Ubuntu Phone 和 Ubuntu Touch

看完上面的内容,我相信有一些“互联网阅历”的朋友,一定会想起 “Ubuntu 移动操作系统”。但之所以不适配这个“更适合手机”的系统版本,而是选择适配标准版、更新的桌面版本,或许了解完它的故事,你会和我做出一样的选择。

曾经的 Ubuntu Touch

在 2011 年,Ubuntu 宣布发布了适用于移动端、平板、电视等设备的分支操作系统:Ubuntu Touch。但是在 2017 年的时候,由于市场反馈不佳,Canonical 宣布停止维护该项目,随后改项目由 2015 年发起的 UBports 项目所在的基金会收购,原本官方支持的项目转变为了社区产品。

目前来看,这个系统支持的设备非常有限,参考资料也并不多,所以就不考虑从这个角度做“Linux 适配”啦。

曾经的 Ubuntu Phone

另外一个有些关联,但是目前看来也步入历史尘埃的项目 Ubuntu Phone,将 Ubuntu 以 Android 系统方式融入手机,并提供使用 “QT、JS、HTML” 定义手机 App 的能力,也因为公司商业化方向调整,而被放弃,时间在 2014 年,比上面的 Touch 项目还更早一些。

自此以后,Ubuntu 官方就基本一心扑在“服务端”、“云市场”、“AI 市场”、“部分消费者日常笔电设备”上了。

第一步:获取预构建的 ARM 架构 Ubuntu 桌面版

回到《MacBook Pro 原生安装 Ubuntu 24.04 ARM 版》这篇文章里,我们使用的是 Asahi 项目的衍生项目 UbuntuAsahi/ubuntu-asahi,翻阅项目文档,我们不难得到项目的安装方法:

curl -sL https://ubuntuasahi.org/install | bash

如果我们将命令末尾的 | bash 删除,再次执行命令,就能够一窥安装脚本的庐山真面目啦:

# curl -sL https://ubuntuasahi.org/install# ------#!/bin/sh
# SPDX-License-Identifier: MIT# Truncation guard
if true; thenset -eexport LC_ALL=en_US.UTF-8export LANG=en_US.UTF-8export PATH="/usr/bin:/bin:/usr/sbin:/sbin:$PATH"export VERSION_FLAG=https://cdn.asahilinux.org/installer/latestexport INSTALLER_BASE=https://cdn.asahilinux.org/installerexport INSTALLER_DATA=https://ubuntuasahi.org/installer_data.jsonexport REPO_BASE=https://files.tobhe.de/ubuntu#TMP="$(mktemp -d)"TMP=/tmp/asahi-installecho "Bootstrapping installer:"if [ -e "$TMP" ]; thenmv "$TMP" "$TMP-$(date +%Y%m%d-%H%M%S)"fimkdir -p "$TMP"cd "$TMP"echo "  Checking version..."PKG_VER="$(curl --no-progress-meter -L "$VERSION_FLAG")"echo "  Version: $PKG_VER"PKG="installer-$PKG_VER.tar.gz"echo "  Downloading..."curl --no-progress-meter -L -o "$PKG" "$INSTALLER_BASE/$PKG"if ! curl --no-progress-meter -L -O "$INSTALLER_DATA"; thenecho "    Error downloading installer_data.json. GitHub might be blocked in your network."echo "    Please consider using a VPN if you experience issues."echo "    Trying workaround..."curl --no-progress-meter -L -O "$INSTALLER_DATA_ALT"fiecho "  Extracting..."tar xf "$PKG"echo "  Initializing..."echoif [ "$USER" != "root" ]; thenecho "The installer needs to run as root."echo "Please enter your sudo password if prompted."exec caffeinate -dis sudo -E ./install.sh "$@"elseexec caffeinate -dis ./install.sh "$@"fi
fi

安装程序比较简单,基本就是定义一些“资源”,然后分别下载“真正的安装程序(installer)”和“安装程序数据(installer_data.json.json)”,然后调用安装程序,完成安装。

让我们使用 curl 来看看安装程序数据文件里都有些什么吧:

# curl https://ubuntuasahi.org/installer_data.json
{"os_list": [{"name": "Ubuntu Desktop 23.10","default_os_name": "Ubuntu","boot_object": "m1n1.bin","next_object": "m1n1/boot.bin","package": "ubuntu-desktop-23.10-20240502.zip","supported_fw": ["12.3", "12.4", "13.5"],"partitions": [{"name": "EFI","type": "EFI","size": "500MB","format": "fat","volume_id": "0xC3F44B40","copy_firmware": true,"copy_installer_data": true,"source": "esp"},{"name": "Boot","type": "Linux","size": "2147483648B","image": "boot.img"},{"name": "Root","type": "Linux","size": "12GB","expand": true,"image": "root.img"}]},
...
}

installer_data.json 中定义的内容,看起来就是我们要找的预构建好的 ARM 架构的系统镜像文件,虽然是 23.10 版本的 Ubuntu,但是我们可以和之前那篇文章一样,尝试升级系统到最新版本嘛。

不过,不论我们怎么和前面的安装程序中,预先定义好的路径进行拼合,就是无法下载到这个 ubuntu-desktop-23.10-20240502.zip 镜像压缩包。看来,需要研究下 Installer 这个隐藏在背后的真正的安装程序啦。

不论是选择使用 curl 模拟上面的逻辑,下载程序到本地,还是直接访问安装程序本体的开源项目 AsahiLinux/asahi-installer,我们都不难定位到位于 src/osinstall.py 的核心函数 load_package

def load_package(self):package = self.template.get("package", None)if not package:returnif not package.startswith("http"):package = os.environ.get("REPO_BASE", ".") + "/os/" + packagelogging.info(f"OS package URL: {package}")if package.startswith("http"):p_progress("Downloading OS package info...")self.ucache = urlcache.URLCache(package)self.pkg = zipfile.ZipFile(self.ucache)else:p_progress("Loading OS package info...")self.pkg = zipfile.ZipFile(open(package, "rb"))self.flush_progress()logging.info(f"OS package opened")

原来在下载镜像的时候,程序会额外地做一个拼合动作,在地址中加一个 /os/ 目录。依样画葫芦,拼合上面得到的信息,得到能够下载的资源地址:

https://files.tobhe.de/ubuntu/os/ubuntu-desktop-23.10-20231107.zip

第二步:分析 ARM 架构镜像中的“素材”

使用 curl 或者下载工具完成预构建镜像的下载后,我们使用 unzip 对其解压缩,来看看里面有哪些东西:

# unzip ubuntu-desktop-23.10-20231107.zipArchive:  ubuntu-desktop-23.10-20231107.zipinflating: boot.img                creating: esp/creating: esp/m1n1/inflating: esp/m1n1/boot.bin       creating: esp/EFI/creating: esp/EFI/ubuntu/inflating: esp/EFI/ubuntu/grub.cfg  inflating: esp/EFI/ubuntu/grubaa64.efi  creating: esp/EFI/BOOT/inflating: esp/EFI/BOOT/BOOTAA64.EFI  inflating: root.img

可以看到镜像压缩包中,主要包含了三部分:

  • boot.img:根据名字来看是主要负责启动的分区镜像,具体内容需要还原分区后验证。
  • esp:用于引导这个镜像的 UEFI 启动分区程序(EFI system partition 的缩写就是 esp
  • root.img:“根分区”镜像,看了下文件大小,足足有 8GB,应该是 Ubuntu 系统本体啦。

第三步:重新划分磁盘分区

上一篇文章中 “对手机进行分区调整” 小节中,我们介绍了如何对手机进行分区划分和处理。不过当时的目标是安装 Windows 系统,和现在的验证 Ubuntu 安装的场景需求不太匹配。

所以,我们来参考上篇文章,并略做调整,折腾出一个适用于上面 Ubuntu 镜像的环境:划分出三个新的分区。

恢复手机分区环境

重启手机并进入 “TWRP” 模式,确保 adb devices -l 能够“看到”你的设备:

# adb devices -l
List of devices attached
42cba029               recovery 1-1 product:raphael model:Xiaomi_Redmi_K20_Pro device:raphael transport_id:118 

首先,还是使用 adb pushadb shell 命令,补全手机恢复环境中没有的 parted 工具,然后对 dev/block/sda 磁盘进行分区更新:

# adb push ./parted /sbin/                                               
# adb shell "chmod +x /sbin/parted"
# adb shell parted /dev/block/sda./parted: 1 file pushed, 0 skipped. 286.2 MB/s (922984 bytes in 0.003s)
GNU Parted 3.5
Using /dev/block/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted)                                                         

和上篇文章提到的一样,我们不要“摸黑”操作,先 print 确认下环境:

# (parted) printprintModel: SAMSUNG KLUFG8R1EM-B0C1 (scsi)
Disk /dev/block/sda: 505GB
Sector size (logical/physical): 4096B/4096B
Partition Table: gpt
Disk Flags: Number  Start   End     Size    File system  Name        Flags1      24.6kB  32.8kB  8192B                switch2      32.8kB  65.5kB  32.8kB               ssd3      65.5kB  98.3kB  32.8kB               dbg4      98.3kB  131kB   32.8kB               bk015      131kB   262kB   131kB                bk026      262kB   524kB   262kB                bk037      524kB   1049kB  524kB                bk048      1049kB  1573kB  524kB                keystore9      1573kB  2097kB  524kB                frp
10      2097kB  4194kB  2097kB               bk05
11      4194kB  8389kB  4194kB               misc
12      8389kB  12.6MB  4194kB               vm-data
13      12.6MB  16.8MB  4194kB               bk06
14      16.8MB  25.2MB  8389kB               logfs
15      25.2MB  33.6MB  8389kB               bk07
16      33.6MB  50.3MB  16.8MB               oops
17      50.3MB  67.1MB  16.8MB               devinfo
18      67.1MB  83.9MB  16.8MB               oem_misc1
19      83.9MB  101MB   16.8MB  ext4         metadata
20      101MB   134MB   32.9MB               bk08
21      134MB   168MB   34.2MB               splash
22      168MB   201MB   33.6MB               bk09
23      201MB   268MB   67.1MB  ext4         persist
24      268MB   336MB   67.1MB  ext4         persistbak
25      336MB   403MB   67.1MB               logdump
26      403MB   537MB   134MB                minidump
27      537MB   671MB   134MB                rawdump
28      671MB   738MB   67.1MB               recovery
29      738MB   1007MB  268MB   ext4         cache
30      1007MB  2080MB  1074MB  ext4         cust
31      2080MB  32.0GB  29.9GB  ext4         userdata
32      32.0GB  32.5GB  499MB                esp         boot, esp
33      32.5GB  485GB   453GB                win         msftdata
34      485GB   505GB   20.3GB  fat32        armpe       msftdata

能够看到分区和上一篇文章中的一致,在 userdata 目录后,保留着我们新划分的 espwinarmpe 三个分区。

先来“恢复现场”,手动删除掉这三个分区,使用 rm + 分区编号即可:

# (parted) rm 32
rm 32# (parted) rm 33
rm 33# (parted) rm 34
rm 34

执行完分区的还原后,使用 p free 查看下分区状态:

30      1007MB  2080MB  1074MB  ext4         cust
31      2080MB  32.0GB  29.9GB  ext4         userdata32.0GB  505GB   473GB   Free Space

系统中由恢复了 473GB 的空余空间。

划分新分区

参考上篇文章中的操作,我们来重新划分 esp 分区,并设置分区可引导:

# (parted) mkpart esp fat32 32GB 32.5GB                                     
mkpart esp fat32 32GB 32.5GB# (parted) set 32 esp on                                                    
set 32 esp on

设置完毕,使用 p free 确认磁盘状况,以及确保上面的操作已经生效:

31      2080MB  32.0GB  29.9GB  ext4         userdata32.0GB  32.0GB  438kB   Free Space
32      32.0GB  32.5GB  499MB   fat32        esp         boot, esp32.5GB  505GB   473GB   Free Space

接下来,我们分别创建用于存放上文中我们得到的 boot.img 分区镜像和 root.img 分区镜像的分区:

mkpart ubuntu-boot fat32 32.5GB 42.5GBmkpart ubuntu-os ext4 42.5GB 505GB

当命令执行完毕,我们会看到类似下面的日志输出:

# (parted) mkpart ubuntu-boot fat32 32.5GB 42.5GB                           
mkpart ubuntu-boot fat32 32.5GB 42.5GB# (parted) mkpart ubuntu-os ext4 42.5GB 505GB                               
mkpart ubuntu-os ext4 42.5GB 505GB

还是使用 p free 确认磁盘状况,确保上面的操作已经生效:

31      2080MB  32.0GB  29.9GB  ext4         userdata32.0GB  32.0GB  438kB   Free Space
32      32.0GB  32.5GB  499MB   fat32        esp          boot, esp
33      32.5GB  42.5GB  10.0GB  fat32        ubuntu-boot  msftdata
34      42.5GB  505GB   463GB   ext4         ubuntu-os505GB   505GB   1028kB  Free Space

好了,重启手机,让分区生效。

第四步:开始还原分区

接下来,我们来尝试将下载的 ARM 架构的镜像还原到上面准备好的手机分区中。

传输文件到手机内部存储

重启手机,等待手机再次进入“TWRP 模式”。

接着,先将刚刚我们下载并解压缩的 Ubuntu 镜像的三个部分传输到手机内的存储。这样可以确保我们在“还原磁盘”的过程中,不会出现数据线断开的麻烦事情。

# adb push /Users/soulteary/Downloads/esp/EFI /data/
# adb push /Users/soulteary/Downloads/boot.img /data/
# adb push /Users/soulteary/Downloads/root.img /data//Users/soulteary/Downloads/esp/EFI/: 4 files pushed, 0 skipped. 27.2 MB/s (5547908 bytes in 0.194s)
/Users/soulteary/Downloads/boot.img: 1 file pushed, 0 skipped. 33.4 MB/s (2147483648 bytes in 61.286s)
...

由于这台手机的 USB 接口是 2.0 协议,传输完毕所有文件,大概需要几分钟。如果你的手机内部存储空间比较有限,你可以先一个一个文件的传输并做还原,在还原后,手动删除掉传完的文件,来节约磁盘空间。

使用 adb shell 可以看到,大概需要消耗手机上 17G 的磁盘空间:

# adb shell df -hFilesystem       Size  Used Avail Use% Mounted on
tmpfs            5.6G  288K  5.6G   1% /dev
tmpfs            5.6G     0  5.6G   0% /mnt
tmpfs            5.6G   32K  5.6G   1% /tmp
/dev/block/sda29 232M  248K  232M   1% /cache
/dev/block/sde52 320M  172M  148M  54% /firmware
/dev/block/sda31  27G   10G   17G  38% /data

使用 dd 还原磁盘

当我们的文件上传到手机内部空间后,我们可以使用 dd 来读取手机内部的文件,并写入上文中创建好的分区中,先来恢复 ubuntu 系统分区:

# dd if=/data/root.img of=/dev/block/by-name/ubuntu-os bs=40962097152+0 records in
2097152+0 records out
8589934592 bytes transferred in 60.627 secs (141684968 bytes/sec)

接着,我们来恢复 boot 分区:

# dd if=/data/boot.img of=/dev/block/by-name/ubuntu-boot bs=4096524288+0 records in
524288+0 records out
2147483648 bytes transferred in 13.729 secs (156419524 bytes/sec)

恢复 ESP 分区

想要初始化 EFI 所在的 esp 分区,我们还需要一些额外的操作。

先使用 mkfs.fat 对我们创建好的 esp 分区进行格式化:

# mkfs.fat -F32 -s1 /dev/block/by-name/espmkfs.fat 3.0.28 (2015-05-16)

接着,创建一个用于挂载 esp 分区的路径,并使用 mount 将分区挂载到系统中:

mkdir -p /mnt/esp
mount /dev/block/by-name/esp /mnt/esp/

现在,我们就能够将我们下载的数据全部都还原到手机里啦。

cp -r /data/EFI/ /mnt/esp/

第一次启动验证(Grub 可以工作)

虽然,我们知道此时此刻,大概率手机是无法正确引导 Ubuntu 的,但是还是可以重启手机,看看我们距离目标还有多远。

重启设备进行验证

我们在手机上看到了熟悉的 Ubuntu Grub 引导界面,说明 GRUB 相关程序可用。

GRUB 引导报错

但是,当读条结束,紧随其后的是一行小字报错:

Invalid header detected on UEFI supplied FDT, ignoring

搜索这个报错,在 Kernel.org 的一次提交记录中,找到了这个 EFI 程序报错:

 	if (fdt_check_header(fdt) != 0) {
-		pr_efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
+		efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");return NULL;}

继续搜索这个函数的名称,在另外一个 Linux 相关的网站上,能够看到这个函数的定义:

// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
/** libfdt - Flat Device Tree manipulation* Copyright (C) 2006 David Gibson, IBM Corporation.*/
...int fdt_check_header(const void *fdt)
{size_t hdrsize;/* The device tree must be at an 8-byte aligned address */if ((uintptr_t)fdt & 7)return -FDT_ERR_ALIGNMENT;if (fdt_magic(fdt) != FDT_MAGIC)return -FDT_ERR_BADMAGIC;if (!can_assume(LATEST)) {if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)|| (fdt_last_comp_version(fdt) >FDT_LAST_SUPPORTED_VERSION))return -FDT_ERR_BADVERSION;if (fdt_version(fdt) < fdt_last_comp_version(fdt))return -FDT_ERR_BADVERSION;}hdrsize = fdt_header_size(fdt);if (!can_assume(VALID_DTB)) {if ((fdt_totalsize(fdt) < hdrsize)|| (fdt_totalsize(fdt) > INT_MAX))return -FDT_ERR_TRUNCATED;/* Bounds check memrsv block */if (!check_off_(hdrsize, fdt_totalsize(fdt),fdt_off_mem_rsvmap(fdt)))return -FDT_ERR_TRUNCATED;/* Bounds check structure block */if (!can_assume(LATEST) && fdt_version(fdt) < 17) {if (!check_off_(hdrsize, fdt_totalsize(fdt),fdt_off_dt_struct(fdt)))return -FDT_ERR_TRUNCATED;} else {if (!check_block_(hdrsize, fdt_totalsize(fdt),fdt_off_dt_struct(fdt),fdt_size_dt_struct(fdt)))return -FDT_ERR_TRUNCATED;}/* Bounds check strings block */if (!check_block_(hdrsize, fdt_totalsize(fdt),fdt_off_dt_strings(fdt),fdt_size_dt_strings(fdt)))return -FDT_ERR_TRUNCATED;}return 0;
}

虽然,我们不熟悉这块代码和程序实现,但是结合注释和返回“非0”(正确)逻辑来看,应该是我们启动设备过程中的“设备树”相关的配置或者程序有问题。将排查目标转向 “设备树” 或许会有突破。

所以,到目前为止,我们知道了这个方案或许可行,GRUB 可以运行,但是设备树的配置存在问题、GRUB 的配置可能也有问题。

什么是设备树(Device Tree)

可能有的同学第一次听到这个名词,因为在本文中非常有用,所以我们简单展开下。(你可以阅读这篇课程“OSD335x Lesson 2: Linux Device Tree”,来了解更多)

简要设备树结构图示意(OSD335x Lesson 2)

Linux 设备树是一种用于描述硬件配置的数据结构,它独立于操作系统的代码之外,能够将硬件的描述与操作系统代码分离,使得内核可以独立,和设备不耦合。

主要的作用和特点有:

  1. 硬件描述:设备树文件(.dts)使用一种可读的文本格式,描述系统中的硬件组件、它们的属性和连接关系。
  2. 可移植性:通过使用设备树,Linux 内核可以在不同的硬件平台上运行,而无需为每个平台定制内核代码。
  3. 动态加载:设备树在系统启动时被加载到内存中,内核可以动态地访问和解析设备树信息。
  4. 设备驱动程序:设备树信息有助于简化设备驱动程序的编写,驱动程序可以通过设备树获取必要的硬件信息。
  5. 二进制格式:设备树源文件需要通过编译工具转换为二进制的设备树 Blob(.dtb),以便内核解析。
  6. 覆盖机制:通过使用设备树覆盖(Device Tree Overlay),可以在运行时动态地修改设备树,而无需重新编译内核。

第五步:分析手机分区数据

为了解决上面的问题,我们首先需要看看恢复的手机分区内容都是什么呢。

还原现场,静态观察

让我们再次重启手机,将处于 TWRP 模式的手机和电脑相连,然后执行 adb shell 进入交互式环境。

为了确保我们排查的文件和上面手机首次运行的一致,这里我们可以再次重复“第四步”中的步骤,来一个“现场恢复”。

然后,依次执行下面的命令,将上文中的几个分区挂载到手机系统中。

mkdir -p /mnt/esp
mkdir -p /mnt/boot
mkdir -p /mnt/ubuntumount /dev/block/by-name/ubuntu-boot /mnt/esp/
mount /dev/block/by-name/ubuntu-boot /mnt/boot/
mount /dev/block/by-name/ubuntu-os /mnt/ubuntu/

接着,我们先来简单查看不同分区中的内容:

# ls /mnt/esp/
System.map-6.5.0-1009-apple-arm efi  initrd.img                      initrd.img.old vmlinuz                      vmlinuz.old 
config-6.5.0-1009-apple-arm     grub initrd.img-6.5.0-1009-apple-arm lost+found     vmlinuz-6.5.0-1009-apple-arm# ls /mnt/ubuntu/
bin boot dev etc home lib lost+found media mnt opt proc root run sbin snap srv sys tmp usr var# ls /mnt/boot/
System.map-6.5.0-1009-apple-arm efi  initrd.img                      initrd.img.old vmlinuz                      vmlinuz.old 
config-6.5.0-1009-apple-arm     grub initrd.img-6.5.0-1009-apple-arm lost+found     vmlinuz-6.5.0-1009-apple-arm# ls /mnt/esp/                                                                                                                                                                     
EFI

/mnt/ubuntu 分区的内容看起来比较正常,目前我们不能确认,也还不需要确认内容是否有问题,可以先跳过,稍后再分析。

/mnt/esp/mnt/boot 分区中的内容,都掌管着设备的启动,尤其是 /mnt/boot 中的文件,看起来有 apple-arm 设备绑定关系,或许需要详细排查。

因为 /mnt/boot 中存在比较多的“冗余文件”,通常情况不需要这么多文件,所以我们不妨先看看,是否存在软链的情况:

raphael:/ # ls -al /mnt/boot/                                                                                                                                                                
total 59764
drwxr-xr-x 5 root root       4096 2023-11-07 07:52 .
drwxr-xr-x 6 root system      120 2024-05-05 16:54 ..
-rw------- 1 root root    5478721 2023-10-12 01:57 System.map-6.5.0-1009-apple-arm
-rw-r--r-- 1 root root     265861 2023-10-12 01:57 config-6.5.0-1009-apple-arm
drwxr-xr-x 2 root root       4096 2023-11-07 07:50 efi
drwxr-xr-x 5 root root       4096 2023-11-07 07:52 grub
lrwxrwxrwx 1 root root         31 2023-11-05 05:54 initrd.img -> initrd.img-6.5.0-1009-apple-arm
-rw-r--r-- 1 root root   43770460 2023-11-07 07:52 initrd.img-6.5.0-1009-apple-arm
lrwxrwxrwx 1 root root         31 2023-11-05 05:54 initrd.img.old -> initrd.img-6.5.0-1009-apple-arm
drwx------ 2 root root      16384 2023-11-07 07:50 lost+found
lrwxrwxrwx 1 root root         28 2023-11-05 05:54 vmlinuz -> vmlinuz-6.5.0-1009-apple-arm
-rw------- 1 root root   11645650 2023-10-12 01:57 vmlinuz-6.5.0-1009-apple-arm
lrwxrwxrwx 1 root root         28 2023-11-05 05:54 vmlinuz.old -> vmlinuz-6.5.0-1009-apple-arm

通过上面的信息,我们可以得到简化上面的文件关系:

System.map-6.5.0-1009-apple-arm
config-6.5.0-1009-apple-arm
efi
grub# 都指向 `initrd.img-6.5.0-1009-apple-arm`
initrd.img     -> initrd.img-6.5.0-1009-apple-arm
initrd.img.old -> initrd.img-6.5.0-1009-apple-arm# 都指向 `vmlinuz-6.5.0-1009-apple-arm`
vmlinuz -> vmlinuz-6.5.0-1009-apple-arm
vmlinuz.old -> vmlinuz-6.5.0-1009-apple-arm

如果这时,我们将手机重启,尝试加载操作系统,然后再重复这一步(第五步)的操作,会发现 /mnt/boot/mnt/esp 分区的内容会变成一致的。

所以,或许我们可以少分析一些内容了。

获取运行后的配置状态

经过反复验证,手机会在启动的时候,将 /boot 目录的内容,完整镜像到 /esp 分区。所以,我们可以再多忽略一个分区的内容(/esp 分区),只对 /boot 分区内容进行分析。

依旧是进入 adb shell 交互终端环境,将 /boot 分区挂载到手机系统中:

mkdir -p /mnt/bootmount /dev/block/by-name/ubuntu-boot /mnt/boot/

然后,可以使用 adb pull 将手机上的分区数据都下载到本地,方便后续的分析:

# adb pull /mnt/boot ./boot/mnt/boot/: 244 files pulled, 0 skipped. 30.0 MB/s (179651614 bytes in 5.703s)

分析下载至本地引导程序的问题

我们根据上文中的信息,将本地的文件和目录去重,将得到类似下面的目录内容:

# du -hs *5.2M	System.map-6.5.0-1009-apple-arm
260K	config-6.5.0-1009-apple-arm0B	efi
7.8M	grub43M	initrd.img11M	vmlinuz

如果你曾经经常安装系统,能够一眼认出来,这不就是 **兼容 UEFI 和 BIOS **的 可引导磁盘/光盘 使用的程序嘛。

直接定位 grub/grub.cfg 中的核心内容,定义启动 Ubuntu 的配置部分:

menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-8fbf3439-8cd7-4734-8fb3-88b8def0a291' {recordfailload_videogfxmode $linux_gfx_modeinsmod gzioif [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fiinsmod ext2search --no-floppy --fs-uuid --set=root b24cb109-0c66-4ea5-bdf3-caf947ed0390linux	/vmlinuz-6.5.0-1009-apple-arm root=UUID=8fbf3439-8cd7-4734-8fb3-88b8def0a291 ro  quiet splash $vt_handoffinitrd	/initrd.img-6.5.0-1009-apple-arm
}

排除掉各种常规设置配置外,下面这两条指令最可能是不能正常运行的 “罪魁祸首”(关于 GRUB 的资料,感兴趣可以阅读 ArchLinux WIKI 的相关文档或者 GNU 的 GRUB 文档):

linux	/vmlinuz-6.5.0-1009-apple-arm
initrd	/initrd.img-6.5.0-1009-apple-arm

在根目录中的 config-6.5.0-1009-apple-arm 配置文件中,我们能够看到生成 vmlinuzinitrd.img 使用的配置,或许,我们可以试试用符合这台手机的 Linux 内核和设备树文件,来生成符合引导程序使用的内容。

第六步:构建可用的引导内容

说到构建可用的引导内容,Intel 有一个很好的参考资料:“构建 UEFI 引导加载程序”,如果你感兴趣,也可以了解。

我们简化构建引导程序内容,尤其是生成包含生成 vmlinuzinitrd.img 产物内容,一般会有以下几个步骤:

  1. 准备编译环境,以及准备某个指定版本内核的 Linux 源码。
  2. 定制内核功能,使用 make menuconfig 生成我们需要的构建配置。
  3. 编译内核源码,得到 bzImage,即:vmlinuz。(参考:为什么 vmlinuz 和 bzImage 是相同的)
  4. 制作 initrd.img 携带初始化启动脚本的微型根文件系统。
  5. 打包文件,更新 grub 配置。

虽然上面的制作引导系统的基本流程看起来非常简单,但是实际操作中还是有一些细节需要注意:

  1. 内核配置项极多;
  2. initrd 中要包含必要的设备驱动和工具,需要寻找补全,并需要控制 initrd 产物的尺寸不要过大(加载不起来);
  3. 胆大信息,反复调试。

至于编译需要的算力,在 soulteary/Home-Network-Note 项目中,列举了我目前的设备状况,即使前几篇文章中提到的能够当高性能 CI/CD 的 ARM Ubuntu 的设备不能完成构建,也有一些其他的可选项(x86),问题不大。

最后

五一假期最后一天,突然想到了这个方法,至于能否复活这个特别的 “Ubuntu Phone”,让它丝滑的运行 Ubuntu 24.04 和 Docker 程序,接下来相关的文章里,我们来一起试试看吧。

–EOF


我们有一个小小的折腾群,里面聚集了一些喜欢折腾、彼此坦诚相待的小伙伴。

我们在里面会一起聊聊软硬件、HomeLab、编程上、生活里以及职场中的一些问题,偶尔也在群里不定期的分享一些技术资料。

关于交友的标准,请参考下面的文章:

苏洋:致新朋友:为生活投票,不断寻找更好的朋友

当然,通过下面这篇文章添加好友时,请备注实名和公司或学校、注明来源和目的,珍惜彼此的时间 😄

苏洋:关于折腾群入群的那些事


本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

本文作者: 苏洋

创建时间: 2024年05月06日
统计字数: 18548字
阅读时间: 38分钟阅读
本文链接: https://soulteary.com/2024/05/06/android-natively-run-arm-ubuntu-24-04-desktop-1.html

相关文章:

安卓手机原生运行 ARM Ubuntu 24.04 桌面版(一)

本篇文章&#xff0c;聊一聊尝试让安卓手机原生运行 Ubuntu&#xff0c;尤其是运行官方未发布过的 ARM 架构的 Ubuntu 24.04 桌面版本。 写在前面 最近的几篇文章&#xff0c;都包含了比较多的实操内容、需要反复的复现验证&#xff0c;以及大量的调试过程&#xff0c;为了不…...

AHB---数据总线

1. 数据总线 为了实现AHB系统&#xff0c;需要独立的读写数据总线。虽然推荐的最小数据总线宽度被指定为32位&#xff0c;但这可以根据数据总线宽度进行更改。 数据总线包含以下部分&#xff1a; HWDATAHRDATAEndianness&#xff08;字节序&#xff09; 1.1 HWDATA 在写传输…...

「51媒体」企业单位新闻稿件考核,怎么发布

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 电力税务企事业单位部门等单位提供了新闻稿件&#xff0c;如何在一些重点媒体进行宣发呢&#xff1a; 精准锁定发布媒体 了解考核要求&#xff1a;仔细阅读宣传任务名单&#xff0c;了解…...

「 网络安全常用术语解读 」SBOM主流格式CycloneDX详解

CycloneDX是软件供应链的现代标准。CycloneDX物料清单&#xff08;BOM&#xff09;可以表示软件、硬件、服务和其他类型资产的全栈库存。该规范由OWASP基金会发起并领导&#xff0c;由Ecma International标准化&#xff0c;并得到全球信息安全界的支持&#xff0c;如今CycloneD…...

React 之 内置标签<Fragment> (<>...</>) (十一)

通常使用 <>…</> 代替&#xff0c;它们都允许你在不添加额外节点的情况下将子元素组合。相当于vue的内置标签<template/> 1. 返回多个元素 <><OneChild /><AnotherChild /> </>2. 分配多个元素给一个变量 和其他元素一样&#xf…...

Mac M1 解决安装grpcio不可用

问题描述&#xff1a; 使用 pip 已经更新 grpcio 至最新版&#xff0c;调用时还是报错 如下图&#xff1a; Traceback (most recent call last):File "/Users/yu/anaconda3/envs/dify2/lib/python3.10/site-packages/flask/cli.py", line 245, in locate_app__imp…...

Linux第三节--常见的指令介绍集合(持续更新中)

点赞关注不迷路&#xff01;&#xff0c;本节涉及初识Linux第三节&#xff0c;主要为常见的几条指令介绍。 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1f44d;&#x1f3fb; 收藏 ✨ 加关注&#x1f440; 期待与你共同进步! Linux下基本指令 1. man指令 Linu…...

SpringMVC简介和体验

一、SpringMVC简介和体验 1.1 介绍 Spring Web MVC :: Spring Framework Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称&#xff08; spring-webmvc &#xff09;&#…...

Android单行字符串末尾省略号加icon,图标可点击

如图 设置仅显示单行字符串&#xff0c;末尾用省略号&#xff0c;加跟一个icon&#xff0c;icon可点击 tvName.text "test"val drawable ResourcesCompat.getDrawable(resources, R.mipmap.icon_edit, null)tvName.setCompoundDrawablesWithIntrinsicBounds(null,…...

山东省文史书画研究会成立20周年系列活动徽标征集胜选名单公布

2024年5月1日&#xff0c;山东省文史书画研究会成立20周年系列活动徽标征集落下帷幕。征稿启事下发后&#xff0c;得到社会各界人士的广泛关注与参与&#xff0c;共收到设计方案608件。经过初评&#xff0c;选出5幅作品进入复评&#xff0c;并经过网络投票和专家投票相结合的方…...

相机2:曝光三要素之ISO(感光度)

曝光是相机的感光元件与光线接触成像的过程&#xff0c;而曝光三要素分别指的是光圈大小&#xff0c;快门速度和感光度。这三个因素都可以控制曝光量&#xff0c;同时也分别有自己的特点。 什么是感光度&#xff1f; ISO又叫感光度&#xff0c;指的是相机感光元件&#xff08;…...

已解决java.util.IllegalFormatConversionException异常的正确解决方法,亲测有效!!!

已解决java.util.IllegalFormatConversionException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 报错原因 示例报错代码&#xff1a; 解决思路 解决方法 检查和更正格式说明符 示例修正代码&#xff1a; 调整参数类型…...

OpenCV 库来捕获和处理视频输入和相似度测量(73)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:OpenCV的周期性噪声去除滤波器(70) 下一篇 :使用 OpenCV 创建视频(74) ​ 目标 如今&#xff0c;拥有数字视频录制系统供您使用是很常见的。因此&#xff0c;您最终会遇到不再处理一批图像&#xf…...

了解TMS运输管理系统,实现物流高效运转

TMS运输管理系统&#xff08;Transportation Management System&#xff09;是一种集成物流和信息技术的解决方案&#xff0c;通过优化运输流程、实时跟踪货物信息和自动化管理操作&#xff0c;提高物流效率&#xff0c;降低运营成本&#xff0c;实现高效运输。 TMS运输管理系…...

数据库原理与应用实验三 嵌套查询

实验目的和要求 加深和掌握对嵌套查询的理解和应用 实验环境 Windows10 SQLServer 实验内容与过程 图书&#xff08;书号&#xff0c;书名&#xff0c;价格&#xff0c;出版社&#xff09; 读者&#xff08;卡号&#xff0c;姓名&#xff0c;年龄&#xff0c;所属单位&a…...

LeetCode 110.平衡二叉树(Java/C/Python3/Go实现含注释说明,Easy)

标签 树深度优先搜索递归 题目描述 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡的二叉树定义为&#xff1a; 一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。 原题&#xff1a;LeetCode 110.平衡二叉树 思路及…...

【SQL】ACID事务与隔离级别

数据库事务 数据库事务具有ACID这4个特性&#xff1a; A&#xff1a;Atomicity&#xff0c;原子性&#xff0c;将所有SQL作为原子工作单元执行&#xff0c;要么全部执行&#xff0c;要么全部不执行&#xff1b;C&#xff1a;Consistency&#xff0c;一致性&#xff0c;事务完…...

深度神经网络中的不确定性研究综述

A.单一确定性方法 对于确定性神经网络&#xff0c;参数是确定的&#xff0c;每次向前传递的重复都会产生相同的结果。对于不确定性量化的单一确定性网络方法&#xff0c;我们总结了在确定性网络中基于单一正向传递计算预测y *的不确定性的所有方法。在文献中&#xff0c;可以找…...

实用的Chrome浏览器命令

Google Chrome 是一款广泛使用的网络浏览器&#xff0c;它提供了许多实用的快捷键和命令&#xff0c;可以帮助用户更高效地浏览网页。以下是一些常用的 Chrome 浏览器命令&#xff1a; 1. 新标签页: Ctrl T (Windows/Linux) 或 Command T (Mac) 2. 关闭当前标签: Ctrl W 或…...

无人作业控制器--4G/5G通信

一、环境 开发环境&#xff1a;ubuntu 22 ros2 humble 发布运行环境&#xff1a;地平线旭日x3派、arm64 4G 模组&#xff1a; 移远EC20模块 5G 模组&#xff1a;移远RG200U-CN 网络通信模组根据需要选择其中一款&#xff0c; 前期我们使用4G模组&#xff0c;后续迭代因为…...

动态规划-两个数组的dp问题2

文章目录 1. 不同的子序列&#xff08;115&#xff09;2. 通配符匹配&#xff08;44&#xff09; 1. 不同的子序列&#xff08;115&#xff09; 题目描述&#xff1a; 状态表示&#xff1a; 根据题意这里的dp数组可以定义为二维&#xff0c;并且dp[i][j]表示字符串t的0到i的…...

如何设置并行度 ——《OceanBase 并行执行》系列 2

并行度&#xff08;degree of parallelism&#xff0c;简称 DOP&#xff09;&#xff0c;是指在执行过程中所使用的工作线程数量。设计并行执行的初衷在于充分利用多核资源以提升效率。OceanBase 的并行执行框架支持多种设定并行度的方式&#xff0c;既允许用户手动设置&#x…...

python直接发布到网站wordpress之三批量发布图片

在前面的文章中&#xff0c;实现了使用python操作wordpress发布文字内容和图片内容。 python直接发布到网站wordpress之一只发布文字-CSDN博客 python直接发布到网站wordpress之二发布图片-CSDN博客 不过&#xff0c;此时发布图片的数量只能是一张图片。但在实际应用中&…...

C#面:ADO.NET 相对于ADO等主要有什么改进

C# ADO.NET 是微软为.NET平台开发的一套数据访问技术&#xff0c;相对于传统的ADO&#xff08;ActiveX Data Objects&#xff09;等&#xff0c;它有以下几个主要改进&#xff1a; 面向对象&#xff1a;ADO.NET 是面向对象的数据访问技术&#xff0c;它使用.NET框架中的类和对…...

web前端学习笔记7-iconfont使用

7. iconfont的使用流程 字体图标使用较多的是阿里巴巴iconfont图标库,它是阿里巴巴体验团队推出的图标库和图标管理平台,提供了大量免费和可定制的矢量图标,以满足网页设计、平面设计、UI设计、应用程序开发和其他创意项目的需求。 官方网站:https://www.iconfont.cn/ 使用…...

国内小白用什么方法充值使用ChatGPT4.0?

首先说一下IOS礼品卡订阅&#xff0c;目前最经济实惠的订阅方式&#xff0c;具体操作步骤 使用IOS设备充值&#xff0c;用 App Stroe 兑换券 1、支付宝地址切换旧金山&#xff0c;在里面买app store 的兑换卷 2、美区Apple ID登陆app store &#xff0c;充值兑换券 3、IOS设…...

富格林:正确杜绝欺诈实现出金

富格林悉知&#xff0c;现货黄金一直以来都是投资者们追逐的热门品种之一。其安全性和避险特性吸引着广大投资者。但在现货黄金市场中要想实现出金其实并不简单&#xff0c;是需要我们通过一定的技巧和方法去正确杜绝欺诈套路。下面为了帮助广大投资者正确杜绝欺诈实现出金&…...

基于java,SpringBoot和VUE的求职招聘简历管理系统设计

摘要 基于Java, Spring Boot和Vue的求职招聘管理系统是一个为了简化求职者与雇主间互动流程而设计的现代化在线平台。该系统后端采用Spring Boot框架&#xff0c;以便快速搭建具有自动配置、安全性和事务管理等特性的RESTful API服务&#xff0c;而前端则使用Vue.js框架构建动…...

sqlserver数据库日志文件log.ldf文件占用过大清除的办法

sqlserver数据库日志文件log.ldf文件占用过大清除的办法 技术交流 http://idea.coderyj.com/ 1.清除数据库日志的方法 --- 查看数据库日志文件名 USE cs GO SELECT file_id, name,size,* FROM sys.database_files;ps 可以看到其中name字段为数据库日志名称"数据库日志名称…...

【Python小技巧】matplotlib不显示图像竟是numpy惹的祸

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、问题&#xff1a;df.plot() 显示不出图像二、尝试各种解决办法1. 增加matplotlib.use&#xff0c;设定GUI2. 升级matplotlib版本 三、numpy是个重要的库1. …...

asp做的网站频繁报错 参数错误/泉州百度网站推广

刚才在论坛不经意间&#xff0c;看到有关源码阅读的帖子。回想自己前几年&#xff0c;阅读源码那种兴奋和成就感(1)&#xff0c;不禁又有一种激动。 源码阅读&#xff0c;我觉得最核心有三点&#xff1a;技术基础强烈的求知欲耐心。 说到技术基础&#xff0c;我打个比方吧&…...

福建省政府门户网站建设情况/关键词优化一年多少钱

Python爬虫视频教程零基础小白到scrapy爬虫高手-轻松入门 https://item.taobao.com/item.htm?spma1z38n.10677092.0.0.482434a6EmUbbW&id564564604865 xkcd 单线程下载图片 抓取网址 下载图片 下载所有图片分别用不同函数 # -*- coding: utf-8 -*- # -*- coding: utf-8 -*…...

青岛景观设计公司排名/百度seo关键词优化电话

在信贷业务的不同阶段有不同的评分卡模型。分别是&#xff1a;贷前A卡-申请评分卡、贷中B卡-行为评分卡、贷后C卡-催收评分卡。三种评分卡起到的作用也是不一样的。本文是基于kaggle的信用卡数据做的信贷申请评分卡。运用的算法是&#xff1a;逻辑回归。工具&#xff1a;SAS EG…...

研创网/搜索引擎优化排名关键字广告

Silverlight是微软公司基于.NET框架构建的技术平台&#xff0c;使IT专业人士能够更加敏捷和高效的开发出下一代富媒体和交互式富互联网应用程序&#xff0c;最大程度的满足客户的需求并且帮助企业和组织最大化收回投入。Silverlight实现了跨浏览器&#xff0c;跨平台和与终端设…...

天河手机网站建设/重庆关键词快速排名

类库包含了许多类例如FutureTask、Semaphore、BlockingQueue等&#xff0c;但是这些玩意又个共同的概念&#xff0c;那就是不能从空的队列中删除元素&#xff0c;这些操作执行之前&#xff0c;必须等队列进入非空状态&#xff0c;或者任务进入已完成状态。 可阻塞状态变量必须由…...

汕头建站费用/网络推广营销方案100例

从一道面试题说起&#xff1a;GET 请求能传图片吗&#xff1f;「前言」曾经遇到的面试题&#xff0c;觉得挺有意思&#xff0c;来说下我的答案及思考过程。首先&#xff0c;我们要知道的是&#xff0c;图片一般有两种传输方式&#xff1a;base64 和 file对象。base64 图片图片的…...