做嵌入式Linux开发的人,十有八九都被“装个软件”这件事恶心过。通用Linux上一条dnf install就能解决的事,在嵌入式板子上经常变成一场灾难。我最近在 openEuler Embedded 上折腾新增软件包,把在线安装和离线安装的路子都试了一遍,踩了不少坑,也把真正能跑通的流程梳理了出来。这篇文章就专门聊聊 openEuler Embedded 新增软件包和在线安装软件这件事,适合正在用或者准备用 openEuler Embedded 做产品开发的工程师参考,尤其适合那些被“无法定位软件包”“软件包似乎无效”这类报错折磨过的朋友。
先说结论:openEuler Embedded 和普通 Linux 发行版的包管理思路差异很大,你没法把它当成 mini 版 CentOS 来用。镜像默认做了大量裁剪,很多通用发行版自带的工具在这里不存在,软件源也不像服务器版那么全。想“新增软件包”,你得先搞清楚系统当前的架构、版本、包管理工具状态,再决定走“离线 RPM”“本地源”还是“在线 dnf”哪条路。这篇文章会把每条路线的原理、步骤、坑点一次讲透。
1. 先搞清楚问题出在哪:openEuler Embedded 的包管理背景
1.1 它和通用 Linux 最大的区别:裁剪、只读、架构差异
openEuler Embedded 是面向嵌入式场景的发行版,虽然继承了 openEuler 的 RPM 体系,但它和服务器版、桌面版完全是两回事。嵌入式系统为了控制镜像体积、减少攻击面、保证启动速度,rootfs 通常是深度裁剪过的,很多命令、库、工具链都不会预装。你和通用 Linux 的习惯性操作,在这里要么找不到命令,要么能跑但一运行就缺库。
比裁剪更麻烦的是只读文件系统。不少 openEuler Embedded 镜像是把根文件系统做成只读或者 overlayfs 的形态,你辛辛苦苦装好一个软件,重启之后发现“回到了解放前”。这不是软件包本身有问题,而是系统的存储布局设计成“重启还原”,你得先搞清楚/的挂载方式,再决定怎么持久化安装结果。
另外还有架构差异。开发机绝大多数是 x86_64,而 openEuler Embedded 的常见目标平台是 aarch64(ARM64)和 riscv64。你在 x86 服务器上dnf install习惯了,拿到板子上手就装,十有八九会碰上“架构不匹配”的报错。这不是软件不存在,而是你选的包根本不是这个 CPU 能跑的。
1.2 包管理工具链现状:rpm、dnf 可能都不完整
openEuler Embedded 继承了 openEuler 的 RPM 体系,但“继承”不等于“完整”。我实际遇到的情况是:镜像里rpm命令经常是有的,但dnf不一定带,yum基本别指望。这就带来一个很现实的问题——rpm本身不会自动解析依赖,没有dnf就缺少“在线安装”的通道。
登录板子之后,我建议你立刻跑下面三组命令,把家底摸清楚:
which rpm dnf yum ls /etc/yum.repos.d/ 2>/dev/null || ls /etc/dnf/ 2>/dev/null cat /etc/os-release如果which输出里只有 rpm,没有 dnf,那你别急着想在线安装,先把“离线 RPM”和“构建新包”这两条路走通。如果 dnf 也在,那后面的在线源方案才真正有戏。无论哪条路,都要先确认系统大版本,因为官方源是按openEuler-22.03-LTS、openEuler-23.09这类版本组织目录的。
1.3 新增软件包的四种路径,先看清再动手
我把 openEuler Embedded 上新增软件包的方法总结为四条路径,你可以按实际环境选:
| 路径 | 适用场景 | 依赖处理 | 复杂度 |
|---|---|---|---|
| 离线 RPM 安装 | 临时应急、网络隔离、包数量少 | 手动处理或提前拉取依赖 | 低 |
| 本地 repo 源(伪在线) | 内网批量部署、多台板子 | 配置 dnf 自动解析 | 中 |
| 在线 repo 源(dnf install) | 能访问外网、官方源有现成包 | dnf 自动解析 | 中 |
| SDK/bitbake 构建 | 官方源没有、需要自定义编译 | 构建时自动处理 | 高 |
这个表格是我实际选型的心得。很多人一上来就想“在线安装”,结果发现源里没有想要的包;也有很多人喜欢直接rpm -ivh硬刚,结果被依赖折磨到怀疑人生。正确的思路是:先看官方源里有没有,有就尽量用 dnf 在线装;没有但有现成 rpm,就离线装;都没有,老老实实去构建系统里现造一个。下面我把每条路径逐一展开。
2. 准备工作:确认环境、锁对软件包来源
2.1 登录板子后第一时间要跑的检查命令
不要一拿到板子就想着装软件,先花两分钟做环境体检。我每次拿到新板子,都会固定执行一套命令,后面能少踩一半的坑。
uname -m # 查看 CPU 架构,找对 rpm 包 cat /etc/os-release # 查看系统大版本,决定用哪个源 df -h / # 确认根分区剩余空间 cat /proc/mounts | grep " / " # 看根文件系统是不是只读/overlay free -m # 确认内存足够,避免大软件装完运行直接 OOM为什么要看前两个?因为嵌入式镜像的架构和版本决定了你所有后续动作。如果你在 aarch64 的板子上装了一个 x86_64 的 rpm,rpm -ivh可能会直接拒绝,也可能装进去之后一运行就是Exec format error。版本不匹配更隐蔽,装进去大概率碰到.so版本冲突,查起来非常费劲。
至于第三、四条,是我吃过亏之后才养成的习惯。有块板子的根分区只有 400MB,缓存一满,dnf install装到一半就报磁盘空间不足,还把系统搞到半残。遇到这种板子,优先清理dnf clean all,或者先把软件包下到内存挂载点、确认能装再往系统里放。
2.2 openEuler Embedded 的软件仓库长什么样
openEuler Embedded 的软件包并不是随便找个 openEuler 源就能装的。官方把嵌入式场景的软件包单独放在了 repo.openeuler.org 站点下,结构上通常按“大版本 / embedded_apps / 架构 / Packages”这样的路径组织。比如 22.03 LTS 的 aarch64 嵌入式软件源,路径大概长这样:
http://repo.openeuler.org/openEuler-22.03-LTS/embedded_apps/aarch64/注意版本号一定要和板子cat /etc/os-release里看到的大版本对齐,架构目录也要选对。这里有个特别容易犯的错误:看到 openEuler 官方源里有很多包,就把标准服务器版的 repo 配到嵌入式板子上。这通常不是你想要的,因为服务器版软件的依赖链路和嵌入式裁剪版不一致,装了很容易把系统搞坏。
嵌入式专用仓库里的包是为裁剪环境重新处理过的,依赖相对干净,针对目标架构(尤其是 aarch64 和 riscv64)也有现成的编译产物。所以在开始之前,去 repo.openeuler.org 看一眼有没有和你系统版本、架构完全匹配的 embedded 目录,这决定你后面是“直接在线装”还是“离线下载”。
2.3 下载软件包前必须确认的三个元信息
不管走哪条路,你在下载/安装一个 rpm 之前,都要先看三个东西:架构(Arch)、版本(Version)、依赖(Requires)。
- 架构:通过
uname -m确认,aarch64 对应 ARM64,riscv64 对应 RISC-V,别下载错。 - 版本:尽量匹配系统大版本,比如系统是 22.03 LTS,就优先找 22.03 系列的包。
- 依赖:用
rpm -qpR查看这个包依赖哪些.so和子包,提前把依赖链拉齐。
如果你手头已经有一个 rpm 文件,在没安装之前就能这样检查:
rpm -qip xxx.rpm # 查看基本信息和架构 rpm -qpR xxx.rpm # 查看依赖列表 rpm -qp --scripts xxx.rpm # 查看安装前/后脚本,这个尤其重要--scripts是我强烈推荐你养成的习惯。嵌入式系统裁剪很狠,rpm脚本里常用的useradd、install、ldconfig等命令不一定存在,脚本一旦失败,安装就会报“post-installation 脚本 子进程返回错误状态”之类的错误。提前看一眼,能预判很多问题。
3. 实操一:离线 RPM 安装——最直接,但也最容易翻车
3.1 下载 rpm 包的几种姿势
如果你确定系统里暂时没有可用的在线源,只能靠离线 rpm,那第一步是找包。我按优先级列出几种方式:
- 官方 repo 站点手动下载:打开 repo.openeuler.org 对应版本和架构的 embedded_apps 目录,按名字找包,
wget下载。 - 在板子上用 dnf 拉包:如果板子上有 dnf,哪怕还没配好源,也可以在后文配好源之后执行
dnf install --downloadonly --downloaddir=/tmp xxx,把包和依赖一起拉到本地目录。 - 在开发机上用 repoquery 检查依赖:如果开发机是 openEuler/CentOS 系列,可以安装
dnf-utils或createrepo_c相关工具,配合仓库查询依赖。
这里有个典型错误:只下载主包,不下载依赖。rpm -ivh时系统会告诉你缺哪个.so,你再去下一个包,装完又告诉你缺另一个库,一层套一层,最后“补依赖补到怀疑人生”。正确做法是下载之前先用rpm -qpR把依赖列清楚,一次性把需要的 rpm 都找齐。
3.2 rpm -ivh 之前,先给你的包做“体检”
把 rpm 通过scp、U 盘、TFTP 等方式传到板子后,不要着急安装。先执行一遍我在 2.3 节说的检查命令,确认架构、版本、依赖、脚本都正常。一切 OK 之后,再正式安装:
rpm -ivh tree-2.0.0-1.aarch64.rpm安装完成后,用这三条命令确认状态:
rpm -q tree # 查询是否安装成功 rpm -ql tree # 查看文件都装到了哪里 which tree && tree /tmp # 实际跑一下,验证二进制能否正常工作很多人在rpm -q显示已安装后就不管了,结果一执行就报error while loading shared libraries。这说明依赖库缺失,rpm安装本身没问题,但运行环境不完整。判断“有没有装上”和“能不能用”是两码事。
3.3 大坑:依赖问题的三种处理原则
依赖问题离线安装中避不开,我总结出三条处理原则:
第一,能一次性把依赖都下载齐,就别裸着用rpm。第二,必须--nodeps时,装完后立刻用ldconfig -v检查动态库情况,缺库赶紧补。第三,永远不要在嵌入式系统里用--force强行覆盖核心包,比如 glibc、openssl、systemd 这一类,一旦覆盖,shell 可能当场崩掉,甚至板子直接起不来。
如果你实在找不到某个依赖,可以用rpm -qpR看一下它具体依赖哪个库文件,再去 openEuler 源里搜索对应库的 rpm。比如报缺少libcrypto.so.3(),那大概率需要补装openssl-libs或对应版本包。这种“按库找包”的思路,在嵌入式环境里比按软件名找包可靠得多。
4. 实操二:配置本地源,实现“伪在线”安装
4.1 在服务器上建本地仓库,一步到位
离线 RPM 装几个包还行,如果板上要装十几个软件、依赖关系错综复杂,手动处理依赖会痛不欲生。这时候我推荐搭建一个本地 repo 源,让 dnf 像在线源一样自动解析依赖。
做法很简单。在一台 Linux 服务器上建目录,把需要的 rpm 全部放进来,然后执行:
mkdir -p /srv/rpm/openeuler-embedded cp *.rpm /srv/rpm/openeuler-embedded/ cd /srv/rpm/openeuler-embedded createrepo .如果服务器上没有createrepo命令,在 openEuler 系列系统上可以用dnf install createrepo_c安装,本质一样。执行完会生成一个repodata目录,这就是 dnf 识别仓库的依据。
4.2 板子上写 repo 文件,把源指过来
仓库建好之后,把整个目录通过 NFS、HTTP 或直接拷贝的方式让板子能访问到。最简单的是在板子上写一个 repo 文件:
mkdir -p /etc/yum.repos.d cat > /etc/yum.repos.d/openeuler-embedded.repo <<'EOF' [embedded-local] name=openEuler Embedded Local Repo baseurl=file:///srv/rpm/openeuler-embedded enabled=1 gpgcheck=0 EOF如果板子和服务器在同一内网,也可以用 HTTP 方式,nginx 或python3 -m http.server起一个静态服务,baseurl写成http://服务器IP:8000。关键在于gpgcheck=0,本地源通常没有对应的公钥,先保证能装上,签名校验等生产环境再严格处理。
配好之后在板子上执行:
dnf clean all dnf makecache dnf list available | head能看到包列表,说明本地源已经生效。之后你就可以像在线安装一样执行dnf install xxx了。
4.3 为什么说本地源在嵌入式环境里最实用
我实际部署过几十块板子,深有体会:很多嵌入式项目根本不允许设备连接公网,所以“在线源”方案在这种环境里就是个摆设。但你又不能靠逐个rpm -ivh装几十个包,依赖问题会写到崩溃。本地源正好卡在两者中间:从有网络的机器上把官方仓库的嵌入式软件目录整个同步下来,或者只同步你要的几十个包,然后在隔离内网里搭一个包服务器,所有板子统一配这个源。维护起来也很顺手,新包只要丢到仓库目录、重新createrepo,板子上dnf update就能看到。
注意:本地源方案的前提是板子上有
dnf。如果镜像连 dnf 都没有,优先考虑用 SDK 构建一个带 dnf 的镜像,否则本地源对rpm命令帮助有限。
5. 实操三:配置在线源,真正实现 dnf install
5.1 找对官方 embedded 仓库地址
在线安装是所有方法里体验最好的,但前提是选对源。不要随手拿一个 apt 源或者标准 openEuler 源来配,必须去官方站点确认嵌入式仓库的实际路径。大版本和架构都要和设备完全对应。
以 openEuler 22.03 LTS、aarch64 为例,repo 文件可以写成这样:
cat > /etc/yum.repos.d/openeuler-embedded.repo <<'EOF' [openeuler-embedded] name=openEuler Embedded baseurl=http://repo.openeuler.org/openEuler-22.03-LTS/embedded_apps/aarch64/ enabled=1 gpgcheck=0 EOF路径里的openEuler-22.03-LTS要和cat /etc/os-release看到的大版本一致,aarch64要和uname -m一致。如果你用的是 riscv64 板子,目录就要切成riscv64。
5.2 板子上配置 repo 并刷新缓存
写完之后,最关键的一步是清缓存、重建元数据:
dnf clean all dnf makecache然后验证源是否真的通了:
dnf list available | grep -i tree如果能搜到tree,说明在线源没问题。接下来就可以直接安装:
dnf install -y tree安装过程如果显示多个依赖包一起被解析、下载、安装,就说明源和仓库都工作正常。注意,dnf install默认会询问是否确认,脚本化部署时一定要加-y。
5.3 在线安装成功后的验证和注意事项
装完别急着收工,按这个顺序验证一遍:
rpm -q tree tree --version rpm -qa | grep tree如果rpm -q显示已安装,但执行tree报缺库,说明依赖还是没解决干净。这时候回去看dnf list available是否漏掉了某些依赖子包,或者源里是否有版本冲突。
还有一点要注意:在线源里搜不到某个包很正常,不要慌。openEuler Embedded 的软件仓库覆盖面比通用发行版小得多,很多桌面级、服务器级软件根本没有嵌入式构建版本。遇到这种情况,就直接跳到下一章用 SDK 自己构建。
6. 实操四:没有现成包?从 SDK 构建一个 rpm 出来
6.1 进入构建环境,找到正确的构建入口
在线源、本地源、离线包都不好使的时候,说明该自己造轮子了。openEuler Embedded 是基于类似 Yocto 的构建体系来生成镜像和软件包的,官方 SDK 里有一套完整的构建环境,入口一般是oe-init-build-env脚本:
source oe-init-build-env进入构建环境后,如果你是往镜像里加一个已有 recipe 的包,可以编辑conf/local.conf,在IMAGE_INSTALL里追加包名,然后重新构建镜像。如果是要为某个新软件创建包,那就需要写一个.bb格式的 recipe 文件,定义源码地址、依赖、安装逻辑,这个稍微有点门槛,但思路和写PKGBUILD或spec文件是相通的。
6.2 bitbake 产出 rpm 并传到板子
假设你要构建一个软件包mypkg,在构建环境里执行:
bitbake mypkg构建完成后,rpm 产物会输出到类似tmp/deploy/rpm/aarch64/的目录下。找到对应的 rpm 文件,传到板子上,按照第三章的方法离线安装,或者放入本地源让 dnf 统一管理。
如果你想跳过“安装到镜像”这一步,只想快速在板子上跑某个工具,也可以直接配置本地源指向同步下来的deploy/rpm目录,这样板子端完全不碰编译,只是消费 rpm,非常干净。
6.3 构建常见坑:网络下载、机器配置、依赖不全
构建系统确实能“造出”官方源里没有的包,但坑也不少。
第一,源码下载。openEuler Embedded 构建时经常要从上游下载源码包,国内外网络状况不稳定的话,很容易卡在Fetch failed。建议设置DL_DIR指向一个共享下载缓存目录,并预先手动下载好常见源码包。
第二,内存和磁盘。bitbake 编译大软件很吃资源,我遇到过几次 OOM,构建进程直接被内核杀掉。建议构建机内存不小于 8GB,磁盘剩余空间至少 20GB,编译临时目录不要用 tmpfs。
第三,MACHINE 配置。构建时必须明确目标机器名(比如 qemuarm64、raspberrypi4-64),不同机器输出的包架构、内核模块都不一样,搞错了生成出来的 rpm 板子根本装不上。
7. 常见问题与排查技巧实录
7.1 “无法定位软件包”的几种真实原因
“无法定位软件包”是我收到最多的问题,但背后原因五花八门。
- 仓库源没配好:
baseurl写错了、目录不存在、网络不通,dnf makecache都过不去。 - 包名和文件名不一致:你要搜的是
tree,但实际 rpm 文件名是tree-2.0.0-1.aarch64.rpm,dnf搜索时用的是包名不是文件名。 - 架构不匹配:源里只有
aarch64的包,板子却是x86_64,dnf当然认为没有可用包。 - 仓库缓存太旧:源里其实有,但
dnf元数据没更新,先执行dnf clean all && dnf makecache。 - 版本不对:你配的是 23.09 的源,系统是 22.03 LTS,依赖关系乱,搜索不到很正常。
遇到这个问题别急着换源,按上面几条逐项排查,80% 的“无法定位”都能定位到具体原因。
7.2 安装脚本报错、依赖死循环怎么破
post-installation 脚本 子进程返回错误状态是嵌入式环境常见问题,原因大多是 rpm 包里的脚本调用了系统里不存在的命令或路径。排查方式就是我在 2.3 节说的rpm -qp --scripts,提前看脚本内容,缺啥命令就补装啥。
依赖死循环也比较经典,比如 A 依赖 B,B 依赖 A,rpm单个安装会提示依赖缺失。解决办法有两个:
# 一起安装两个包,rpm 可以同时解析 rpm -ivh A.rpm B.rpm # 或者临时跳过依赖检查,装完马上验证可用性 rpm -ivh --nodeps A.rpm--nodeps是双刃剑,能用但要用在刀刃上。装完立刻ldd验证二进制,不要拖。
7.3 常见错误速查表
| 错误现象 | 常见原因 | 解决思路 |
|---|---|---|
| 无法定位软件包 / 没有可用软件包 | 源没配、架构不符、包名不对 | 核对 baseurl、uname -m,dnf makecache |
| 软件包似乎无效 | rpm 损坏、下载不完整 | 重新下载,或用rpm -K校验 |
| post-installation 脚本返回错误状态 | 包脚本依赖命令缺失 | rpm -qp --scripts预检,补齐依赖工具 |
| 依赖不足 / error while loading shared libraries | 只装了主包,依赖链没拉齐 | 用 dnf 在线装或提前下载全部依赖 |
| 磁盘空间不足 | 嵌入式分区太小 | dnf clean all,必要时扩容根分区 |
| Exec format error | 架构不匹配,装了错误的 CPU 架构包 | 用uname -m确认,重新下对应架构 rpm |
这张表是我在几十次踩坑后浓缩出来的,基本覆盖了嵌入式软件包安装 80% 以上的报错场景。
最后再分享一个经验:在 openEuler Embedded 上装软件,最忌讳的就是“沿用服务器习惯”。先确认架构、版本、包管理工具、磁盘布局这四件事,再选安装路径。官方源有包,就配置在线源用 dnf;没网络,就建本地源;源里没有,就用 SDK 构建。过程不复杂,但顺序错了,每一步都会很难受。希望这篇文章能让你少走点弯路。