☰
嵌入式开发中基于Ubuntu Base构建ARM64根文件系统实战
2026/10/28 1:07:23 网站建设 项目流程

1. 为什么要在嵌入式开发中自己构建Ubuntu根文件系统

做过嵌入式Linux的朋友都知道,拿到一块ARM64开发板之后,最头疼的往往不是内核编译,而是根文件系统。厂商给的BSP包里通常附带一个已经打包好的根文件系统镜像,可能是Buildroot生成的精简版,也可能是Yocto构建的定制版,但当你真正想在上面跑一些稍微复杂的东西——比如Python3的完整环境、Docker、图形界面、或者某些只提供deb包的商业软件——就会发现那个根文件系统缺胳膊少腿,缺库、缺工具、缺包管理器,补起来比重做还费劲。

这就是为什么我后来转向了用Ubuntu Base来构建ARM64根文件系统这条路。Ubuntu Base是Canonical官方提供的一个极简根文件系统压缩包,本质上就是一个能跑起来的最小Ubuntu,里面只有最基础的目录结构和apt包管理器,没有内核、没有引导程序,但你可以chroot进去,用apt随意安装你需要的任何软件包。它和桌面版Ubuntu、服务器版Ubuntu共享同一套软件源,这意味着你装出来的根文件系统,软件生态和你在PC上用的Ubuntu完全一致。

这个方案解决的核心问题是:在ARM64嵌入式设备上获得一个功能完整、可自由扩展、与主流Ubuntu生态兼容的根文件系统。它特别适合以下几类人:正在做ARM64开发板产品原型的嵌入式工程师、需要在嵌入式设备上运行复杂应用(如AI推理、视频处理、边缘计算)的开发者、以及想学习根文件系统构建原理但不想从BusyBox一步步手搓的学生和爱好者。

我这次用的环境是Ubuntu 22.04 LTS x86_64主机,目标架构是ARM64,最终根文件系统会通过QEMU验证后再部署到真实开发板上。整个过程涉及交叉架构的chroot、QEMU用户态模拟、软件源配置、系统裁剪等环节,踩过的坑不少,下面一步步拆开讲。

2. 整体方案设计与核心思路拆解

2.1 为什么选Ubuntu Base而不是Buildroot或Yocto

嵌入式领域构建根文件系统,主流方案有三个:Buildroot、Yocto、以及直接用发行版的Base包。Buildroot胜在轻量,编译出来的根文件系统可能只有几MB,适合资源极度受限的场景,但它的软件包管理很弱,想加个新软件往往要重新编译整个系统。Yocto功能强大、可定制性极高,但学习曲线陡峭,一个完整的构建动辄几小时,而且配置复杂,对新手不友好。

Ubuntu Base的优势在于平衡。它不像Buildroot那么精简,基础包解压后大约30-50MB,但换来的是完整的apt生态。你可以像在PC上一样apt install任何软件,不用重新编译整个系统。对于ARM64开发板来说,现在主流芯片(如瑞芯微RK3588、树莓派CM4、NXP i.MX8系列)的存储至少16GB起步,跑一个完整的Ubuntu根文件系统完全没压力。而且Ubuntu官方长期维护ARM64的软件源,安全更新和版本升级都有保障。

另一个关键考量是开发效率。用Buildroot时,每次加一个软件包都要改配置、重新编译、重新打包,一轮下来半小时起步。用Ubuntu Base,chroot进去apt install几秒钟搞定。在产品原型阶段,这种效率差异是决定性的。

2.2 整体构建流程的架构设计

整个构建过程可以拆成五个阶段,我用一个表格把每个阶段的输入输出和关键操作列出来:

阶段输入关键操作输出
准备阶段主机Ubuntu、网络安装QEMU、debootstrap等工具可用的构建环境
下载解压Ubuntu Base压缩包下载ARM64版base包并解压基础根文件系统目录
配置阶段基础根文件系统配置DNS、软件源、挂载点可chroot的系统
chroot构建可chroot的系统QEMU模拟下apt安装软件定制化的根文件系统
打包部署定制化系统清理、压缩、生成镜像可烧录的根文件系统

这个流程的核心思路是在x86主机上模拟ARM64环境。因为Ubuntu Base是ARM64架构的二进制,x86主机无法直接执行里面的程序,所以需要QEMU的用户态模拟(qemu-user-static)来翻译指令。chroot配合QEMU,就能让ARM64的apt在x86主机上正常运行,这是整个方案的技术基石。

2.3 关键工具选型与理由

QEMU用户态模拟(qemu-user-static):这是让x86主机能执行ARM64二进制文件的关键。qemu-user-static是静态编译的,不依赖主机库,可以直接拷贝到目标根文件系统里使用。相比qemu-system的全系统模拟,user-static模式性能更好,因为它只翻译用户态指令,不需要模拟整个硬件。

debootstrap vs 直接解压Base包:debootstrap可以从零开始构建一个Debian/Ubuntu系统,但它需要从网络下载所有包,速度慢且依赖网络稳定。Ubuntu Base压缩包是官方预构建好的最小系统,下载解压即可用,省去了大量下载时间。我选后者,因为更快更可控。

chroot而非容器:Docker等容器方案也能实现类似效果,但chroot更轻量,不需要额外的守护进程,而且能直接操作文件系统,对嵌入式场景更直观。容器方案在需要多架构支持时配置更复杂,chroot配合QEMU的binfmt_misc注册,一条命令就能切换架构。

注意:qemu-user-static的binfmt_misc注册是全局的,注册后主机上所有ARM64二进制都会自动用QEMU执行。这在构建时很方便,但如果主机上同时有其他ARM64任务,可能会互相干扰。构建完成后可以用update-binfmts --disable qemu-aarch64临时禁用。

3. 核心细节解析与实操要点

3.1 Ubuntu Base版本选择与下载

Ubuntu Base的官方下载地址在cdimage.ubuntu.com的ubuntu-base/releases/目录下。截至我写这篇文章时,最新的LTS版本是24.04(Noble),但22.04(Jammy)在嵌入式领域更成熟,很多BSP包和驱动都是针对22.04验证的。我建议优先选LTS版本,非LTS版本支持周期只有9个月,不适合产品开发。

下载时要注意文件名格式:ubuntu-base-22.04.4-base-arm64.tar.gz。其中arm64是架构标识,一定要确认清楚。曾经有同事误下了armhf版本,结果chroot进去所有程序都报"Exec format error",排查了半天才发现是架构不对。

下载完成后,用sha256sum校验一下完整性。官方会提供SHA256SUMS文件,校验这一步不能省,我遇到过下载过程中网络中断导致压缩包损坏的情况,解压时报错才发现。

# 下载Ubuntu Base 22.04 ARM64 wget https://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04.4-base-arm64.tar.gz # 校验完整性 wget https://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/SHA256SUMS sha256sum -c SHA256SUMS --ignore-missing

3.2 解压与目录结构规划

解压之前先规划好目录。我习惯在~/arm64-rootfs下操作,最终生成的根文件系统放在~/arm64-rootfs/rootfs目录。这样做的好处是构建过程中的临时文件、脚本、日志都在同一个父目录下,方便管理和清理。

解压时必须用sudo,因为Ubuntu Base里包含设备节点文件(如/dev/null),普通用户没有权限创建。解压命令用tar -xzf,注意-p参数保留权限,否则某些可执行文件的权限会丢失。

mkdir -p ~/arm64-rootfs/rootfs sudo tar -xzf ubuntu-base-22.04.4-base-arm64.tar.gz -C ~/arm64-rootfs/rootfs

解压完成后,用ls看一下目录结构,应该能看到bin、etc、usr、var等标准Linux目录。这时候的根文件系统还不能直接chroot,因为缺少QEMU模拟器和必要的挂载点。

3.3 QEMU用户态模拟的配置

这是整个构建过程中最关键的一步。qemu-user-static的安装很简单,apt install qemu-user-static即可,但配置binfmt_misc才是重点。binfmt_misc是Linux内核的一个功能,它能根据文件头识别二进制格式,并自动调用对应的解释器。注册QEMU后,当你执行一个ARM64二进制时,内核会自动用qemu-aarch64-static来运行它。

安装qemu-user-static后,binfmt_misc通常会自动注册。可以用ls /proc/sys/fs/binfmt_misc/检查,如果看到qemu-aarch64文件,说明注册成功。如果没有,手动注册:

sudo update-binfmts --enable qemu-aarch64

然后把qemu-aarch64-static拷贝到根文件系统的/usr/bin/下。这一步不能省,因为chroot后,根文件系统内部执行ARM64程序时,需要这个模拟器在系统内部可用。

sudo cp /usr/bin/qemu-aarch64-static ~/arm64-rootfs/rootfs/usr/bin/

实操心得:有些教程会让你把qemu-aarch64-static放到/usr/bin/下就行,但实测发现如果根文件系统的/usr/bin/权限不对,chroot后会报"Permission denied"。解压后确认一下/usr/bin/的权限是755,属主是root。

3.4 挂载点与DNS配置

chroot之前需要挂载几个关键目录,让根文件系统能正常访问主机的设备和网络:

sudo mount -t proc /proc ~/arm64-rootfs/rootfs/proc sudo mount -t sysfs /sys ~/arm64-rootfs/rootfs/sys sudo mount -o bind /dev ~/arm64-rootfs/rootfs/dev sudo mount -o bind /dev/pts ~/arm64-rootfs/rootfs/dev/pts

/proc和/sys是内核提供的虚拟文件系统,apt运行时需要读取系统信息。/dev和/dev/pts是设备节点,apt安装某些包时需要访问/dev/null、/dev/random等。

DNS配置经常被忽略,但它是apt能否联网的关键。Ubuntu Base默认没有/etc/resolv.conf,需要从主机拷贝一份:

sudo cp /etc/resolv.conf ~/arm64-rootfs/rootfs/etc/resolv.conf

注意,如果主机用的是systemd-resolved,/etc/resolv.conf可能是个软链接,直接拷贝会拷到链接本身。用cp -L强制拷贝实际内容,或者手动写入nameserver:

echo "nameserver 8.8.8.8" | sudo tee ~/arm64-rootfs/rootfs/etc/resolv.conf echo "nameserver 114.114.114.114" | sudo tee -a ~/arm64-rootfs/rootfs/etc/resolv.conf

3.5 软件源配置与架构确认

Ubuntu Base默认的软件源是ports.ubuntu.com,这是ARM64等非x86架构的官方源。但国内访问ports源速度很慢,建议换成国内镜像。清华、中科大、阿里云都有ARM64的ports镜像。

# 备份原配置 sudo cp ~/arm64-rootfs/rootfs/etc/apt/sources.list ~/arm64-rootfs/rootfs/etc/apt/sources.list.bak # 替换为清华源 sudo tee ~/arm64-rootfs/rootfs/etc/apt/sources.list << EOF deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-updates main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-backports main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ jammy-security main restricted universe multiverse EOF

这里有个细节:源地址必须用ubuntu-ports而不是ubuntu。ubuntu是x86架构的源,ARM64用不了。曾经有新手直接抄了x86的源配置,结果apt update报"404 Not Found",排查半天才发现是源地址错了。

4. 实操过程与核心环节实现

4.1 chroot进入ARM64环境

挂载和配置完成后,就可以chroot了。chroot命令本身很简单,但进入后需要做一些初始化工作:

sudo chroot ~/arm64-rootfs/rootfs /bin/bash

进入后,先确认架构是否正确:

uname -m # 应该输出 aarch64

如果输出的是x86_64,说明QEMU没有生效,检查/usr/bin/qemu-aarch64-static是否存在、binfmt_misc是否注册。

接下来更新软件源并安装基础工具。Ubuntu Base太精简了,连vim、ping、ifconfig都没有,先装一批常用工具:

apt update apt install -y vim net-tools iputils-ping sudo ssh systemd

这里有个坑:systemd在chroot环境下无法正常启动,安装时可能会报错或卡住。如果只是构建根文件系统,可以先不装systemd,等部署到真实设备后再装。或者用apt install -y systemd --no-install-recommends跳过推荐包,减少冲突。

4.2 安装常用软件与依赖处理

根据你的应用场景,安装不同的软件包。如果是做AI推理,装Python3、numpy、opencv;如果是做视频处理,装ffmpeg、gstreamer;如果是做网络服务,装nginx、redis。

# 基础开发工具 apt install -y build-essential cmake git python3 python3-pip # 网络工具 apt install -y curl wget openssh-server # 根据需求选装 apt install -y ffmpeg python3-opencv

安装过程中注意观察依赖关系。ARM64的软件包依赖和x86略有不同,某些包在ARM64上可能没有预编译版本,需要从源码编译。比如某些Python的C扩展包,pip安装时会尝试编译,如果根文件系统里没有gcc和python3-dev,就会失败。

实操心得:pip安装时加--no-cache-dir可以节省空间,嵌入式设备存储有限,pip的缓存目录可能占几百MB。另外,如果某个包在ARM64上没有wheel,pip会从源码编译,这时候需要确保build-essential和对应的-dev包已安装。

4.3 系统裁剪与体积优化

Ubuntu Base装完常用软件后,体积可能膨胀到1-2GB。对于嵌入式设备,这个体积可以接受,但如果存储紧张,可以做一轮裁剪:

# 清理apt缓存 apt clean apt autoremove -y # 删除文档和man页 rm -rf /usr/share/doc/* rm -rf /usr/share/man/* # 删除不需要的语言包 apt install -y localepurge localepurge

裁剪时要小心,不要删掉运行时需要的库。/usr/share/doc和/usr/share/man删掉没问题,但/usr/lib下的库文件不能乱动。我一般只做apt clean和autoremove,文档和man页占的空间其实不大,删了收益有限,反而可能影响某些工具的帮助功能。

4.4 用户配置与网络设置

根文件系统默认只有root用户,直接部署到设备上不太安全。创建一个普通用户并配置sudo权限:

useradd -m -s /bin/bash embed passwd embed usermod -aG sudo embed

网络配置方面,Ubuntu 22.04用netplan管理网络。在根文件系统里创建/etc/netplan/01-netcfg.yaml:

network: version: 2 ethernets: eth0: dhcp4: true

注意,这个配置在chroot环境下不会生效,只有部署到真实设备、systemd启动后才会应用。如果设备用的是静态IP,把dhcp4: true改成addresses和gateway4配置。

4.5 退出chroot与打包

所有配置完成后,退出chroot:

exit

然后卸载之前挂载的目录,顺序很重要,先卸载子目录再卸载父目录:

sudo umount ~/arm64-rootfs/rootfs/dev/pts sudo umount ~/arm64-rootfs/rootfs/dev sudo umount ~/arm64-rootfs/rootfs/proc sudo umount ~/arm64-rootfs/rootfs/sys

打包成tar.gz:

cd ~/arm64-rootfs/rootfs sudo tar -czf ../ubuntu-arm64-rootfs.tar.gz .

如果要做成ext4镜像文件,可以用dd创建空镜像,然后mkfs.ext4格式化,再挂载拷贝:

dd if=/dev/zero of=rootfs.img bs=1M count=2048 mkfs.ext4 rootfs.img mkdir -p /mnt/rootfs sudo mount -o loop rootfs.img /mnt/rootfs sudo cp -a ~/arm64-rootfs/rootfs/* /mnt/rootfs/ sudo umount /mnt/rootfs

4.6 QEMU全系统验证

打包完成后,强烈建议用QEMU全系统模拟验证一下根文件系统能否正常启动。这一步能提前发现很多问题,比如init程序缺失、库依赖错误、网络配置问题等。

qemu-system-aarch64 \ -M virt \ -cpu cortex-a57 \ -smp 2 \ -m 1024 \ -kernel Image \ -append "root=/dev/vda rw console=ttyAMA0" \ -drive file=rootfs.img,format=raw,if=virtio \ -nographic

这里的Image是ARM64内核镜像,需要提前编译好。如果只是验证根文件系统,可以用Ubuntu官方提供的ARM64内核。启动后如果能看到登录提示,说明根文件系统基本可用。

5. 常见问题与排查技巧实录

5.1 chroot报"Exec format error"

这是最常见的问题,原因通常是QEMU没有正确配置。排查步骤:

  1. 确认/usr/bin/qemu-aarch64-static存在于根文件系统中
  2. 确认主机上binfmt_misc已注册:ls /proc/sys/fs/binfmt_misc/qemu-aarch64
  3. 确认根文件系统的/usr/bin/qemu-aarch64-static有可执行权限
  4. 确认下载的Ubuntu Base是arm64版本,不是armhf或amd64

如果以上都正常,尝试重新注册binfmt_misc:

sudo update-binfmts --disable qemu-aarch64 sudo update-binfmts --enable qemu-aarch64

5.2 apt update报"404 Not Found"

软件源地址错误。ARM64必须用ports.ubuntu.com或对应的国内ports镜像,不能用普通的archive.ubuntu.com。检查sources.list里的地址是否包含ubuntu-ports。

另一个可能原因是Ubuntu版本代号写错了。22.04的代号是jammy,20.04是focal,24.04是noble。写错了会报404。

5.3 apt install报"Unable to locate package"

先确认apt update是否成功执行。如果update成功但install报找不到包,可能是该包在ARM64上没有预编译版本。用apt search查一下,或者去packages.ubuntu.com上确认该包的ARM64支持情况。

某些包只在universe或multiverse仓库里,确认sources.list包含了这些仓库。

5.4 根文件系统启动后卡在"Waiting for root device"

这是部署到真实设备时常见的问题,原因通常是内核没有对应的存储驱动,或者root=参数指定的设备不对。检查内核配置里是否编译了对应的驱动(如virtio_blk、mmc_block、nvme等),以及/etc/fstab里的设备名是否正确。

如果是NFS挂载根文件系统,检查内核是否支持NFS,以及root=/dev/nfs nfsroot=...参数是否正确。NFS v3和v4的配置略有不同,v3需要nfsvers=3参数。

5.5 常见问题速查表

问题现象可能原因解决方法
Exec format errorQEMU未配置检查binfmt_misc和qemu-aarch64-static
apt update 404源地址错误改用ubuntu-ports源
找不到软件包架构不支持确认包的ARM64支持情况
启动卡住内核驱动缺失检查存储驱动和root参数
网络不通netplan配置错误检查yaml格式和网卡名
权限拒绝文件属主错误用chroot --userspec或修复权限

避坑技巧:构建过程中所有操作都用sudo,但解压后的文件属主是root,如果你在主机上直接编辑根文件系统里的文件,可能会改变属主。建议所有编辑操作都在chroot内部用vim进行,或者用sudo编辑。

6. 部署到真实开发板的注意事项

根文件系统构建完成后,部署到真实开发板还有几个关键点。首先是内核匹配,根文件系统里的库和工具是针对特定glibc版本编译的,如果开发板内核太老,可能不支持某些系统调用。建议用和根文件系统同版本的内核源码编译内核。

其次是存储介质,eMMC、SD卡、NAND Flash的驱动不同,确认内核里编译了对应的驱动。如果是通过U-Boot引导,bootargs里的root=参数要指向正确的分区。

最后是首次启动配置,根文件系统第一次启动时会生成SSH密钥、初始化systemd,这个过程可能需要几分钟。如果设备没有串口输出,可能会以为卡死了,其实是在后台初始化。耐心等一会儿,或者接串口看日志。

我在实际项目里用这套方案构建的根文件系统,已经稳定运行在RK3588和i.MX8M Plus两块开发板上,跑Python AI推理和ffmpeg视频转码都没问题。唯一需要注意的是,每次更新软件包后要重新打包,版本管理要做好,不然容易出现"这台设备能跑那台跑不了"的情况。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询