尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ARM开发板Ubuntu Base根文件系统制作:从解包到启动的完整实战

ARM开发板Ubuntu Base根文件系统制作:从解包到启动的完整实战 最近做了一块T113的板子底软客户要求跑Ubuntu 20.04说看好APT装包方便、生态全以后直接在上面跑Qt应用。一开始我还想着跟以前一样用Buildroot编个根文件系统出来结果客户拿来一堆deb包要装我当场就排除了Buildroot。后来换成Ubuntu Base 20.04做底子从零开始手工搭根文件系统一次成功顺便把过程中踩的坑都记下来了。这篇东西就是完整的实操记录给正在跟ARM开发板、根文件系统死磕的同行一个参考。先交代一下我用的环境。目标板是全志T113-S3Cortex-A7双核32位ARM架构2GB DDR38GB eMMC网口、串口、WiFi模块都有。宿主机是x86_64的Ubuntu 20.04桌面版直接用来解包和chroot。你要是用i.MX6ULL、Zynq、Rockchip之类的板子也完全能照着做区别只在内核和设备树部分根文件系统的构造逻辑是通用的。1. 为什么是Ubuntu Base先搞懂根文件系统这件事1.1 开发板的启动链和根文件系统的位置ARM开发板启动跟PC不一样大多数ARM板子启动顺序是片上ROM - BootloaderU-Boot居多- 内核 - 根文件系统。前三步其实各家方案都差不多真正决定“你这个系统能干嘛”的是最后一步——根文件系统。根文件系统不是一个分区那么简单它是Linux系统运行时的整套目录骨架和基础文件集合包括/bin、/etc、/lib、/usr这些目录以及启动第一个进程init或systemd所需的全部文件。内核启动到最后会调用init进程如果init找不到、库缺失、配置文件错系统就会卡住或者直接panic。内核做得再多没有根文件系统也只是一个空壳。我见过不少初学者把根文件系统当成“装个系统”看待以为拷进去一个.tar.gz解压就行。实际操作确实就是解压但解压之后要做的事比解压本身重要十倍正确配置inittab或systemd单元、网络服务、串口登录、动态库匹配、文件系统格式对齐哪一步错了都表现为启动失败而且报错信息往往特别迷。这套逻辑搞懂之后你就会明白为什么Ubuntu Base这种“最小根文件系统压缩包”方案最适合拿来魔改。它不是一个完整安装好的系统镜像而是一个标准的rootfs目录树解压到目标分区上就能当根文件系统用剩下的东西内核、Bootloader、分区表你自己配就行。1.2 Ubuntu Base vs Buildroot vs Yocto vs Debian轻量系统先用一张表说清楚几套主流方案的差别避免选错方向方案优点缺点适用场景Buildroot定制性强、体积小、构建快装包全靠重新编译包管理弱量产固件、功能固定的设备Yocto深度定制、层次化、可复现学习曲线陡、构建耗时商业产品、需要长期维护的BSPDebian轻量系统debootstrap包管理完善、源丰富初始系统比Ubuntu Base略杂服务器类应用、需要minimal基础Ubuntu Base 20.04生态大、APT现成、文档多体积比Buildroot大部分源慢需要现场装依赖、跑业务系统的板子我这次选Ubuntu Base核心原因就两条一是客户现场要装一大堆deb包APT能直接解决依赖问题不用我一个个编译二是20.04Focal Fossa是长期支持版本2025年之前都有安全更新产品生命周期内不用频繁升大版本。Buildroot不是不好我前几个项目也用它做出来的系统确实小4MB都装得下。但一旦启动后需要装新软件Buildroot的方案就得重新配置编译烧录一次成本太高。客户是做设备的现场不给你机会慢吞吞编译。1.3 架构选择armhf还是arm64先确认再动手ARM软硬件生态有个老坑32位和64位系统不一样甚至同样叫ARM指令集版本都不一样。Ubuntu Base提供两个ARM变体armhf是32位ARM硬浮点arm64是64位AArch64。怎么确认你的板子是哪种两个办法看CPU型号手册或者看现有系统里uname -m输出。uname -m输出armv7l基本是32位输出aarch64那就是64位。如果板子跑着Androidadb shell getprop ro.product.cpu.abi能告诉你abi类型armeabi-v7a对应armhfarm64-v8a对应arm64。看内核版本字符串Linux version 5.4.0-t113这种如果带了arm字样且没提aarch64多半是32位。我这个T113-S3是Cortex-A7纯32位所以选armhf。你要是拿树莓派3以后或者RK3568这类板子一定要下arm64两个架构的软件源、库、编译工具链互相不通用。选错架构你就会遇到一种诡异现象包能装、目录也都在但一执行程序就报Exec format error后面排查章节我会细讲。2. 动手前的准备镜像下载、工具链与宿主环境2.1 获取Ubuntu Base 20.04清华镜像直达Ubuntu Base不像桌面版那样有官方.iso下载入口它是以ubuntu-base-20.04.x-base-$(架构).tar.gz的形式发布放在各大镜像站的cdimage/ubuntu-base/releases/目录下。我用的镜像地址是清华源https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/ubuntu-base/releases/20.04/release/这个目录下面有这些文件ubuntu-base-20.04.5-base-arm64.tar.gz64位ARMubuntu-base-20.04.5-base-armhf.tar.gz32位ARMubuntu-base-20.04.5-base-amd64.tar.gzx86_64一般用不上但存在注意20.04.x最后一个小版本会变2024年的最新到20.04.5你下载的时候以镜像站实际列表为准。下载之前我建议顺手把.tar.gz的校验文件也下下来用sha256sum -c核对一遍。有过镜像同步不完整导致tar包解压到一半报unexpected EOF的情况下载完先验证不浪费时间。2.2 宿主环境准备解包、chroot和两个关键工具在x86主机上制作ARM根文件系统需要两样东西debootstrap虽然是手动解包但后续可能用到它的环境初始化功能和qemu-user-static用QEMU用户态模拟在x86上运行ARM程序。# 宿主机安装依赖 sudo apt update sudo apt install -y debootstrap qemu-user-static binfmt-support # 执行QEMU二进制注册脚本让binfmt_misc自动关联ARM程序 sudo update-binfmts --enable qemu-arm第二个工具的用途解释一下chroot进入ARM rootfs之后如果不装QEMU你在里面执行任何ARM指令都会报Exec format error。装上qemu-user-static并注册到内核的binfmt_misc机制内核看到ARM格式的ELF文件会自动调QEMU来转译执行这样就能在x86主机上跑ARM的apt等命令完成包安装和配置。拿T113这种armhf的板子对应的模拟器是qemu-arm-staticarm64板子就要qemu-aarch64-static。一个常见的翻车点是只装了qemu-user没装qemu-user-static导致chroot里动态编译的ARM程序能跑、但静态链接的/usr/bin/qemu-*找不到后面我会说怎么排。2.3 确认目标板内核与根文件系统的匹配关系根文件系统不是独立存在的它必须跟你的内核匹配。有几个东西在动手之前就要确认内核模块路径。把你要编译好的内核放进去之前确认/lib/modules/下有没有对应的$(uname -r)目录。你的板子内核版本是5.4那rootfs里/lib/modules/应该出现5.4.0-xxx之类。没有模块目录不影响基础启动但网卡、USB、声卡这些驱动模块加载不了的话外设全部瘫痪。firmware固件。WiFi、蓝牙这类外设除了内核模块通常还依赖firmware文件在rootfs的/lib/firmware/里。Ubuntu Base干干净净不带你任何firmware所以我是从linux-firmware这个包里抽取目标文件塞进去的。如果你的板子WiFi模块在启动后lsusb能看到设备但ip link不出现十有八九就是缺少firmware。init机制。Ubuntu 20.04默认用systemd内核启动参数里要有init/lib/systemd/systemd或者不写init参数让systemd的软链接接管。如果你习惯旧的SysV方式可以改成init/sbin/init但Ubuntu 20.04对SysV支持不算完整我个人不建议老老实实systemd最省心。3. 根文件系统制作全流程从解包到能开机3.1 建目录、解开rootfs压缩包、配置QEMU模拟环境流程开始。先把rootfs解压到一个工作目录我是放在~/arm-rootfs下面。# 创建根目录 sudo mkdir -p ~/arm-rootfs # 解压注意用绝对路径避免hab权限混乱 cd ~/arm-rootfs sudo tar -xzf ~/Downloads/ubuntu-base-20.04.5-base-armhf.tar.gz # 解压后确认目录结构 ls ~/arm-rootfs # 应该看到 bin boot dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var解压完把QEMU模拟器副本放进去这是chroot能运行ARM程序的关键# armhf用qemu-arm-staticarm64用qemu-aarch64-static sudo cp /usr/bin/qemu-arm-static ~/arm-rootfs/usr/bin/然后准备chroot需要的虚拟文件系统。chroot之后进程看不到宿主机的/proc、/sys、/dev但这些在应用运行时是必需的必须把宿主机的挂载进来sudo mount --bind /dev ~/arm-rootfs/dev sudo mount --bind /dev/pts ~/arm-rootfs/dev/pts sudo mount -t proc /proc ~/arm-rootfs/proc sudo mount -t sysfs /sys ~/arm-rootfs/sys # 备选有些环境还需要tmpfs保险起见挂上 sudo mount -t tmpfs tmpfs ~/arm-rootfs/tmp这一步有个非常容易踩的坑把proc/sys/dev这四样挂载进去之后退出chroot时一定要记得卸载umount否则宿主机上这些位置会被占住。我一般习惯在做完所有修改后统一卸载但如果你中途想重新chroot先把旧挂载清掉再重新挂。# 退出chroot后清理挂载 sudo umount ~/arm-rootfs/tmp sudo umount ~/arm-rootfs/sys sudo umount ~/arm-rootfs/proc sudo umount ~/arm-rootfs/dev/pts sudo umount ~/arm-rootfs/dev3.2 chroot进入系统第一次配置的核心操作挂载完毕后正式进入rootfssudo chroot ~/arm-rootfs /bin/bash进去之后你是root。第一个要做的事是配置软件源Ubuntu Base默认自带的源是http://archive.ubuntu.com/ubuntu/在国内速度慢得离谱必须换成国内源。用vim或nano改/etc/apt/sources.list# 备份原文件 cp /etc/apt/sources.list /etc/apt/sources.list.bak # 重写成清华源focal对应20.04 cat /etc/apt/sources.list EOF deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse EOF改完立刻apt update试试。出现Certificate verification failed或者Could not resolve八成是DNS没配置或者ca-certificates缺了先临时配上DNS再说echo nameserver 223.5.5.5 /etc/resolv.conf这里用223.5.5.5和114.114.114.114都行阿里和114都能用。注意这个resolv.conf如果之后系统里装了systemd-resolved会被软链接覆盖掉所以配置网络服务的时候要多看一眼后面再说。3.3 基础系统配置locale时区分区挂载和用户locale与中文支持。这一步和后面“终端乱码”问题直接挂钩。Ubuntu Base默认没有生成任何locale如果你直接用它启动板子中文显示全都是菱形方块。先在chroot里安装中文locale并设置默认值apt install -y locales # 生成 zh_CN.UTF-8 和 en_US.UTF-8 sed -i s/^# *\(zh_CN.UTF-8\)/\1/ /etc/locale.gen sed -i s/^# *\(en_US.UTF-8\)/\1/ /etc/locale.gen locale-gen # 设置默认locale为中文UTF-8 update-locale LANGzh_CN.UTF-8 LANGUAGEzh_CN:zh LC_ALLzh_CN.UTF-8如果你不想系统里到处是中文提示有些程序英文提示反而更稳可以把LANG设成en_US.UTF-8但LC_ALL不要设成中文否则个别程序会卡编码。我这里客户端要中文界面所以设成了中文UTF-8后面乱码问题完全靠字体补足locale本身不会再出问题。时区。开发板一般按北京时间配ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime echo Asia/Shanghai /etc/timezone不设时区的话系统默认UTC日志时间跟本地时间差8个小时排查问题的时候容易怀疑人生。root密码与新建用户。Ubuntu Base默认没有设置root密码你要是不设串口登录时只能靠无密码访问sshd会因此拒绝root密码登录。稳妥做法# 设置root密码 passwd root # 新建普通用户 useradd -m -s /bin/bash yourname passwd yourname # 给普通用户加sudo权限 apt install -y sudo usermod -aG sudo yournamefstab分区表。fstab是根文件系统自动挂载其他分区的核心配置。我的板子有eMMC、SD卡两个存储我是这样配的cat /etc/fstab EOF # file system mount point type options dump pass /dev/mmcblk0p2 / ext4 defaults,noatime 0 1 /dev/mmcblk0p1 /boot vfat defaults 0 2 tmpfs /tmp tmpfs defaults 0 0 tmpfs /var/log tmpfs defaults 0 0 EOF对ARM开发板/tmp挂tmpfs能显著减少eMMC/SD卡写入延长Flash寿命/var/log挂tmpfs看需求如果你要持久化日志就不要挂tmpfs否则重启日志全丢。p2和p1的编号取决于你自己的分区方案别照抄先看你的分区情况再写。3.4 网络配置不要被Ubuntu 20.04默认的netplan坑到Ubuntu 20.04引入了netplan作为网络配置前端默认用systemd-networkd或者NetworkManager作为后端。很多嵌入式工程师习惯改/etc/network/interfaces改完发现不起作用就是因为systemd-networkd默认接管了。我先说最简单可控的方案直接用/etc/network/interfaces外加ifupdown来管关掉Netplan的影响。# 在chroot里安装ifupdown apt install -y ifupdown # 配置静态IP cat /etc/network/interfaces EOF auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.1.100/24 gateway 192.168.1.1 dns-nameservers 223.5.5.5 EOF然后禁用systemd-networkd自启systemctl disable systemd-networkd systemctl disable systemd-resolvedsystemd-resolved禁用之后/etc/resolv.conf会被还原成普通文件而不是指向/run/systemd/resolve/的软链接这时候你前面手动写的nameserver 223.5.5.5才有意义。这个坑我踩得很疼第一次没禁resolved明明/etc/resolv.conf写着DNS一重启就被软链接干掉了解析域名永远失败。如果板子要用DHCP把地址配置部分改成auto eth0 iface eth0 inet dhcp串口登录配置。开发板不像PC有显示器和键盘大多数时候靠串口终端登录。如果你的板子内置LCD屏幕和触摸屏那另说调试期一定把串口getty配好否则主板没有画面输出时你完全没法进系统。Ubuntu 20.04用systemd管理getty服务默认只给tty1分配getty。ARM开发板的调试串口通常是ttyS0、ttyS1、ttyAMA0或ttyUSB0要根据你的板子原理图确定。配法# 以ttyS0为例启用串口getty服务 systemctl enable serial-gettyttyS0.service如果想验证serial-getty服务存在tty设备号写错可以先查一下板子内核日志dmesg | grep tty。ttyS0对很多Allwinner全志板子有效i.MX的板子一般是ttytymxc0或者ttymxc0树莓派是ttyAMA0。写错了会表现为“串口完全无输出”或者卡在登录界面无法输入用ls /dev/tty*对比确认最快。内核启动参数console。这个和getty容易混我分开说。内核参数里的consolettyS0,115200是内核打印日志用的它不负责登录交互登录交互是serial-getty服务负责。所以正确做法是内核参数给一行consolettyS0,115200rootfs里再启用serial-gettyttyS0.service两者配合才能实现串口既能看到启动日志又能输账号密码登录。3.5 安装内核模块、firmware和常用工具现在rootfs的基本骨架已经能启动但离“能用”还差很多。内核模块和firmware的安装方法取决于你的内核是怎么来的如果你用的是板厂BSP编译好的内核那会有/lib/modules/$(uname -r)目录直接把这个目录整个拷到rootfs的/lib/modules/下。如果你自己编译内核make modules_install INSTALL_MOD_PATH/path/to/rootfs一步到位。交叉编译内核和模块的场景推荐直接设变量make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- modules_install INSTALL_MOD_PATH~/arm-rootfsfirmware方面全志T113内置了WiFi模块通常是XR829或RTL8723DS这两种芯片的firmware不在Linux主线内核里要从板厂BSP源码找。这也是一个特别容易卡住的地方模块insmod成功但WiFi network设备不出现在ip link里因为你没把对应firmware拷到/lib/firmware/。去板厂SDK的firmware目录找.bin文件拷过来之后权限设644。基础工具我一般装这么一组# 嵌入式板上常用的最低限度工具集 apt install -y vim-tiny htop iftop ethtool net-tools iputils-ping curl wget \ openssh-server rsync file tree less usbutils pciutils kmod \ udev ca-certificates # 清理apt缓存缩小镜像体积 apt clean apt autoremoveudev很重要没有它设备节点靠手动mknod几乎没法用装了udev才能在/dev下自动创建设备。openssh-server用于远程登录装完要注意默认配置是允许root密码登录的测试阶段无所谓上生产前一定改/etc/ssh/sshd_config里PermitRootLogin prohibit-password并配置好SSH公钥。# 安装老版本的Qt或者自定义编译程序时的依赖视情况加 apt install -y libgl1-mesa-dev libinput-dev libts-dev这块看你的实际APP来不必求全。3.6 清理rootfs并打包成可复用的镜像系统配置好之后别急着拔电。rootfs里会残留一些临时文件、日志、chroot时的缓存应清理并重新打包成tar.gz方便备份和量产。# 退出chroot前清理 rm -rf /tmp/* rm -rf /var/tmp/* rm -rf /var/log/* rm -rf /var/cache/apt/archives/*.deb history -c然后退出chroot卸载挂载打包exit sudo umount ~/arm-rootfs/tmp sudo umount ~/arm-rootfs/sys sudo umount ~/arm-rootfs/proc sudo umount ~/arm-rootfs/dev/pts sudo umount ~/arm-rootfs/dev sudo tar -C ~/arm-rootfs -czf ~/ubuntu-base-t113-rootfs.tar.gz .后续烧录时把分区格式化成ext4解压这个tar到分区安装U-Boot就能启动。也可以把这个tar塞进SD卡的第二分区第一分区放内核和设备树用U-Boot从SD卡启动烧录和验证都非常快。4. 常见问题排查实录我踩过的坑一次说清楚这一节是标题里“附常见问题解决”的重点我按开发板根文件系统最常见的几类故障分类每个都写了排查命令和解决办法。这里面的情况都是我在做T113和之前几块RK、i.MX板子时真实遇到过的不是网上抄来的理论。4.1 串口SSH有输出但完全不出登录提示符这个是开发板接串口最常见的问题。如果你已经看到内核日志一路刷到Reached target Login Prompts但还是不出login:提示符多半是getty没被正确启动或者串口设备名选错。排查方法# 进入rootfs查看serial-getty服务状态用chroot或开机后执行 systemctl status serial-gettyttyS0 systemctl list-units | grep getty如果serial-gettyttyS0不存在那可能是被gettytty1占用了你要在bootloader的启动参数里加上systemd.default_standard_outputttyS0或者直接启用serial-gettyttyS0。还有一个隐蔽的原因你启用的ttyS0名字没错但内核把串口枚举到了ttyS1特别是多个串口且其中一个被蓝牙占用时对比一下/dev下实际生成的节点就知道。4.2 串口中文乱码、LCD终端乱码但SSH正常这个命中热搜词里的“imx6ull开发板在屏幕终端中文显示乱码但是在mobaxterm可以显示中文”。我第一次做中文界面也遇到了。首先区分两个原因SSH/MobaXterm显示正常说明locale和字符集没问题问题出在串口终端程序的编码解码上。比如你在Windows上用secureCRT或者MobaXterm看串口如果你的终端设置成UTF-8且locale是zh_CN.UTF-8正常情况不该乱码乱码的话检查终端软件连接串口时有没有把编码设成GBK或者自动识别失败。LCD终端fbcon乱码通常是rootfs里没有安装中文字体。fbcon控制台在显示非ASCII字符时需要字体位图比如fonts-wqy-zenhei文泉驿正黑。装上字体后还要跑一遍fc-cache重启终端才会生效。还有一个我踩过的坑控制台字体要用setfont激活否则有字体文件也白搭。# 安装中文字体 apt install -y fonts-wqy-zenhei fonts-wqy-microhei # 刷新字体缓存 fc-cache -f另外如果你在Qt界面里显示中文乱码那就不是fbcon的事是Qt的字体路径配置没找到字库把文泉驿的ttc文件放到Qt的字体路径下或者在app里指定QFontDatabase::addApplicationFont()加载即可。4.3 Kernel panic - not syncing: VFS: Unable to mount root fs这是所有ARM开发板玩家最噩梦的报错。本质是内核挂载根文件系统失败原因五花八门但八成逃不出这三类第一类启动参数里root设备的设备名和实际分区对不上。比如你写root/dev/mmcblk0p2但内核用的是SD卡驱动先注册实际上SD卡就是mmcblk1那mmcblk0就不存在了。排查办法是进U-Boot的printenv bootargs看看实际的root参数如果内核有挂载失败的提示会在panic上面几行打出VFS: Cannot open root device mmcblk0p2告诉你它找的是哪个设备。第二类根文件系统格式与内核不匹配。你fstab里ext4但内核没编进EXT4驱动或者驱动是模块且没放进/initramfs。嵌入式板子为了精简很多内核没开initramfs模块也都编译进内核而不是独立模块如果模块方式且没做initramfs重启就必然找不到。解法要么把EXT4编译进内核CONFIG_EXT4_FSy要么做initramfs。第三类设备树传递给内核的存储控制器状态不对。常见于eMMC需要调电压、需要IO域配置结果设备树没配导致MMC控制器初始化失败内核看不到存储设备。这种问题dmesg会有明确报错比如mmc0: error -110 whilst initialising MMC card优先检查设备树里mmc节点的vmmc-supply、bus-width、non-removable属性。4.4 有网口但IP起不来、eth0消失或改名为enp...我见过不少人在启动后看到eth0不见了变成enx00e04c123456或者enp1s0一脸蒙。这是systemd的网卡命名规则搞的鬼一致网络设备命名。嵌入式板子强烈建议用传统命名方法# 在rootfs里建立udev规则 cat /etc/udev/rules.d/80-net-name-slot.rules EOF # 禁用可预测网络接口命名保留传统ethX SUBSYSTEMnet, ACTIONadd, DRIVERS?*, ATTR{address}, KERNELeth*, NAMEeth0 EOF如果你的网卡MAC号是已知的固定值也可以在bootloader或内核cmdline里加net.ifnames0 biosdevname0这个参数直接告诉内核别用新命名规则用回eth0。IP起不来的另一个常见原因就是前面网络配置里说的netplan和/etc/network/interfaces冲突。Ubuntu 20.04默认启动时systemd-networkd会去读/run/systemd/network/下的配置而你手动写的interfaces文件默认由networking.service读取两个服务争抢同一个物理网卡的结果是一个生效一个失败。解决办法就是像我前面那样二选一要么全用interfacesifupdown并禁用networkd要么全用netplan。# 彻底停掉netplan和networkd如果你走interfaces方案 systemctl disable systemd-networkd systemd-resolved systemctl enable networking systemctl enable ifupdown4.5 chroot执行apt时报Exec format error或无法执行二进制文件这个问题有两个不同层次的触发点我分开讲chroot进入后连apt都跑不了报错bash: /usr/bin/apt: cannot execute binary file: Exec format error。说明你没有放QEMU模拟器进去或者放错仿真器了。armhf的rootfs里必须放qemu-arm-staticarm64的必须放qemu-aarch64-static两者不能混用。chroot里能跑apt但装某些包含固件或架构特定脚本的包时报同样错。有些deb包在后置脚本postinst里面直接执行了编译好的可执行文件比如在armhf架构下运行的固件打包脚本。如果那二进制是纯x86的或者架构判断错了就会报Exec format error。可以先file /path/binary看它的架构再用readelf -h确认是否是目标架构ELF machine字段应为ARM或AArch64。# 在x86宿主机上确认QEMU注册状态 cat /proc/sys/fs/binfmt_misc/qemu-arm # 如果显示disabled需要重新注册 sudo update-binfmts --enable qemu-arm4.6 用交叉编译器编的程序在板子上跑不起来热搜词里反复出现arm compiler 5.06和arm linux相关的内容我顺带说一嘴这个老坑。板子上跑不起来除了架构不匹配往往还有几个边界情况动态库缺失交叉编译时链接的库是arm-linux-gnueabihf/lib下的但rootfs里没装对应的runtime库。用arm-linux-gnueabihf-readelf -d your_program | grep NEEDED能看到依赖了哪些.so再在rootfs里apt search补齐。硬浮点和软浮点不匹配armhf是硬浮点用的是gnueabihf工具链如果编译时用了gnueabi软浮点或软硬浮点混合在没有硬浮点内核支持的板子会报Illegal instruction。用readelf -A看你的二进制里Tag_ABI_VFP_args是不是VFP registers。glibc版本不一致交叉编译工具链里的glibc比目标rootfs新或者旧运行时会报version GLIBC_2.29 not found。Ubuntu 20.04默认glibc是2.31如果板子根文件系统是18.04那glibc就低了交叉编译时要用--sysroot指定rootfs里的头文件和库来对齐版本。4.7 数据写入丢失、sync时崩溃或文件系统损坏最后说一个偏底层的坑很多人会误以为跟根文件系统没关系实际关系极大。全网热词里也有“根文件系统、sync、vfs”说明大家都没少被折磨。启动后往开发板写入文件重启后丢失或者报EXT4-fs error (device mmcblk0p2): ext4_lookup: deleted inode referenced多半是这几种情况分区未干净卸载Linux对ext4这种日志文件系统要求干净卸载非正常掉电后需要fsck。开发板很多没有电池备份供电直接断电再上电日志没回放完就panic。解决方案是确保fstab里根分区挂载选项带上dataordered并定期fsckuboot里也可以加rootwait和fsck.repairyes内核参数。eMMC/SD卡质量问题板子用的低速卡写入不稳跑高负载时掉盘。用dmesg | grep mmc看有没有mmc0: Timeout或者Card removed这类报错。遇到过很多次是劣质TF卡导致的随机丢数据最后换正品卡问题消失。sync和buffer刷写时机Ubuntu默认有周期性pdflush写回但如果频繁断电数据还在page cache里就丢了。嵌入式系统建议在关键业务写完文件后主动sync或者在fstab里加sync挂载选项会显著降低写性能但换数据安全。# 修改fstab挂载选项增加数据可靠性 # /dev/mmcblk0p2 / ext4 defaults,noatime,dataordered,commit5 0 1 # 手动检查文件系统在开发板引导到另一个系统时执行 fsck.ext4 -f /dev/mmcblk0p2如果你用的是真·量产设备建议额外考虑UBIFS这类专门为NAND Flash设计的文件系统或者对eMMC打开discardtrim支持。这些方式比单纯靠ext4的日志机制更抗掉电。5. 这套方案后续还能怎么扩展我自己做完这个rootfs之后最大的感受是这套玩法根本不是一步到位的它是个基底工程。后续还做了不少事这里挑几个跟开发板强相关的方向分享。移植Qt 5到Ubuntu Base上。热搜里有人搜“qt5.5.10 arm linux开发”那个版本太老了Qt 5.15 LTS之后官方就对嵌入式做了很多优化。在Ubuntu Base里直接apt install qtbase5-dev qtdeclarative5-dev非常省事不需要自己编译Qt库。想控制体积再用qtchooser选模块。T113这种单核A7板子跑Qt 5的简单界面还是能胜任的别上重型QML动画就行。做只读根文件系统。如果你的产品不需要用户改系统可以把rootfs挂在只读分区把可写目录/var、/etc、/home挂到tmpfs或单独数据分区。这样断电永远不伤根系统也杜绝了用户把系统搞崩的可能性。代价是需要把网络配置、hostname这类运行时变化的文件改成systemd的RuntimeDirectory机制来管理。交叉编译工具链匹配。Ubuntu 20.04的armhf仓库里直接有gcc-arm-linux-gnueabihf装完之后用arm-linux-gnueabihf-gcc编译的ELF直接能在T113上跑。如果板子是64位ARM选gcc-aarch64-linux-gnu。工具链版本和系统glibc版本对齐问题用--sysroot指定rootfs就能解决。这比自己编译交叉工具链省至少半天时间。固件升级方案。用Ubuntu Base还有个好处——你可以在rootfs里挂一个完整的OTA升级脚本因为APT本身就能做到增量升级。量产设备上我一般分成两个分区一个active区放当前系统一个inactive区放待升级版本启动时由U-Boot根据标志位决定从哪个分区启动。升级就是解压新的rootfs tar到inactive分区然后翻转标志位重启。这套方案实现成本最低稳定性也好。一次性把Ubuntu Base 20.04做出可启动的ARM开发板根文件系统整条链路基本就是确认架构 - 下载解包 - chroot模拟 - 配置系统源/网络/用户/串口 - 塞内核模块和firmware - 清理打包 - 上板验证。每个环节都有对应的坑但逻辑理顺之后其实不复杂。我个人的实操建议是做rootfs的过程中要时刻用ls、mount、df去确认每一步的结果是否正确。这东西没法靠“感觉”判断看到目录不对、权限错乱、挂载缺失立刻停下来查不要继续往下走。多练两次从下载镜像到能在板子上跑出登录提示符顺利的话一两个小时就能完成。这套底子做好之后后面不管是跑Qt应用、加业务逻辑还是做OTA升级都会非常顺手。
返回列表