简介:面向嵌入式Linux开发者的i.MX6开发环境搭建指南,以Yocto项目为主线,详细覆盖从Ubuntu系统版本选择、依赖软件包安装、Repo工具配置到环境初始化与编译的完整流程,特别适合因常规Qt5移植后无法运行eglfs而决定转向Yocto方案的开发者。包内为1个docx文档,约4.12MB,内容按安装准备、Repo设置、Yocto工程初始化与编译等阶段组织,给出Ubuntu 14.04/12.04下的安装要点、120G硬盘空间建议、必备依赖包命令、非root用户权限处理、Repo获取及国内镜像替代方式,命令与说明并排呈现,可直接对照执行。文档还针对Ubuntu 12.04 git版本过低、下载Repo需要访问Google等实际障碍提供了替代思路,并给出可复制的初始化与同步命令。目前已有1080人学习下载,适合刚接触i.MX6的嵌入式开发者快速搭建可用环境,减少环境配置中的反复试错成本。
1. imx6的Yocto环境先想清楚再动手
很多拿到imx6核心板的人都会先搜“imx6开发环境搭建”,然后一头扎进Yocto的文档里。这件事的复杂度不在bitbake命令本身,而在主机环境、BSP分支和构建配置三者的耦合:同一个MACHINE值在不同DISTRO下会拉出不同的GPU驱动和weston版本,同一份源码在Ubuntu 18.04和22.04上也会有OpenSSL库兼容性的差异。我在搭建imx6 Yocto环境时最直观的感受是,只要第一次正确地把repo同步干净、把local.conf里的缓存路径指向独立磁盘,后续的kernel和rootfs迭代完全可以做到分钟级。这里就把从空白Ubuntu到产出SD卡启动镜像的完整路径写清楚,适合正在做NXP i.MX6平台驱动移植的工程师,也给需要在多台机器间复用构建缓存的人一些可落地的参数。
2. 为imx6的Yocto构建准备主机环境与基础依赖
imx6的Yocto构建本质是在主机上跑一套BitBake任务流:先下载数百个上游源码包,再用交叉编译工具链把它们编成目标平台的二进制,最后通过rootfs组装出镜像。主机环境只要有一项不满足,任务就会在某个package的configure阶段以奇怪的方式失败,比如gcc版本太高导致stdio.h里的glibc符号检查报错,又或者缺了chrpath导致后续打包阶段找不到rpath工具。
2.1 磁盘、内存与Ubuntu版本怎么选
我一般优先用Ubuntu 20.04 LTS作为构建机系统。原因不是它最新,而是Yocto的Kirkstone和Dunfell发行版在20.04上被验证得最多;22.04的OpenSSL 3.0会让一些使用旧签名脚本的BSP在执行rootfs的加密钩子时报EVP_PKEY_CTX相关的接口错误。如果你已经用了22.04,不是一定失败,但如果同一套源在20.04上正常,就不要在22.04上耗时排查。
磁盘方面,一个imx6的full镜像完整构建后,deploy目录约15GB,tmp/work里的解包源码和工作目录可达60GB,加上downloads和sstate,整机预留180GB比较安心。我曾在120GB的机器上构建imx6,最后在打包rootfs的do_image_wic任务上被空间耗尽中断,那种进度损失比慢还难受。机械硬盘也不是不行,但Yocto有大量小文件随机读写,会让构建时间从3小时拉到6小时以上,建议至少用SATA SSD。
| 项目 | 最低要求 | 推荐配置 | 说明 |
|---|---|---|---|
| 操作系统 | Ubuntu 18.04 / 20.04 | Ubuntu 20.04 LTS | 22.04 在部分BSP上有OpenSSL兼容问题 |
| 磁盘 | 120GB | 180GB SSD | tmp/work占大头,deploy约15GB |
| 内存 | 8GB | 16GB | 并行任务多时gcc开销大 |
内存按并行任务数线性增长。bitbake默认会根据CPU核心数起任务,每个gcc任务约占500MB到1GB内存。16GB内存配合4到6个任务是比较稳的配置;8GB机器强行构建会出现virtual memory exhausted: Cannot allocate memory,解决方式不是加swap,而是把PARALLEL_MAKE降为-j 2。另外一个容易忽略的点是/tmp分区也要留出至少10GB,某些包在编译时会直接在TMPDIR里做临时文件写入。
常见做法里有人用Docker容器来隔离构建环境,减少污染宿主机。但Yocto的构建需要访问/dev下的loop设备用于wic镜像的mkfs操作,容器里要额外给--device映射,而且sstate缓存挂载到容器时要注意属主和umask问题。普通单机开发直接用宿主机构建,维护成本更低。
2.2 用apt安装基础依赖包
Ubuntu 20.04最小安装后,需要先把Yocto所需的基础工具补齐。NXP官方手册里的依赖清单针对Ubuntu版本做了区分,常见的做法就是一次性装全,避免在某次构建中途发现缺chrpath或者zstd:
sudo apt update sudo apt install -y gawk wget git-core diffstat unzip texinfo \ build-essential chrpath socat cpio python3 python3-pip \ python3-pexpect xz-utils debianutils iputils-ping python3-git \ python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 xterm \ repo rsync curl zstd这里逐项说明一下关键包的作用:gawk比默认的mawk更贴近Yocto中大量使用的awk语法;chrpath用于修改二进制文件的rpath,在打包SDK时几乎必用;socat被runqemu脚本用来建立串口和网络隧道;python3-git和python3-jinja2是bitbake服务端解析配置所依赖的Python库。repo在Ubuntu的universe源里有,但如果apt装不上,也可以跳过这一项改用下载脚本的方式。
安装完成后用下面三组命令确认版本:
gcc --version | head -n 1 python3 --version repo --versiongcc的版本要8.x以上,python3要3.8以上。目前Ubuntu 20.04自带的gcc 9和python 3.8都满足。如果repo --version提示找不到命令,说明apt源里没有打包,就转到下一步手动安装repo脚本。
注意:不要在host机器上使用系统自带的python2,也不要手工把默认python3改成python2,Yocto从Dunfell开始已经全面切换到python3。
2.3 单独安装repo脚本的方式
apt源里的repo可能停留在较老版本,在执行repo sync时会出现Unknown command syntax这类解析错误。所以更稳的方式是手动放repo脚本到/usr/local/bin:
curl -sSL https://storage.googleapis.com/git-repo-downloads/repo -o /usr/local/bin/repo chmod +x /usr/local/bin/repo repo --version参数说明:-sSL中的三个flag分别表示静默、跟随重定向、出错时显示错误,用于保证下载过程不输出多余进度且能拿到最终内容。脚本不依赖安装包,运行时自动查找系统的git、python3。首次执行repo命令时,它会自动生成~/.repoconfig目录用于存放同步状态,这些残留文件在重新初始化不同manifest时需要手动删除,否则可能出现缓存的认证信息指向旧服务器。
到这里,主机环境基本就绪。下一章进入源码树的拉取阶段,这也是整个imx6开发环境搭建中最容易被网络和分支问题卡住的一步。
3. 用repo拉取imx6的Yocto BSP源码树与选定分支
3.1 manifest与分支命名规则
NXP的i.MX Yocto BSP在github的nxp-imx组织下维护了imx-manifest仓库,里面以XML文件的方式记录每一个组件的git仓库地址和revision。执行repo init只需要解析一份manifest,之后repo sync会按这份清单把上百个仓库并行克隆到本地。理解这个机制,你就知道为什么改分支比切分支更干净——直接在master分支上看不到的meta层,只有在指定manifest后才会被拉到。
分支命名规则有一个需要区分的点:imx-linux-kirkstone是Yocto发行版分支,对应Yocto 4.0;imx-linux-dunfell对应Yocto 3.1。NXP再按季度发布带版本号的manifest,比如imx-5.15.71-2.2.0.xml固定了kernel 5.15和u-boot 2022.04的组合。日常开发如果不想追新,直接锁定某个LTS版本xml是更稳妥的。
| manifest分支 | Yocto版本 | kernel示例 | 适用场景 |
|---|---|---|---|
| imx-linux-dunfell | 3.1 | 5.4.x | 老项目维护、Qt 5.15兼容性稳定 |
| imx-linux-kirkstone | 4.0 | 5.15.x | 新设计推荐,支持周期更久 |
初始化源码树的命令我建议把工作目录和构建目录分开,目录名不要带空格:
mkdir -p ~/imx6-yocto && cd ~/imx6-yocto repo init -u https://github.com/nxp-imx/imx-manifest \ -b imx-linux-kirkstone -m imx-5.15.71-2.2.0.xml参数说明:-u指定manifest仓库的git地址;-b指定要跟踪的分支;-m指定该分支下的xml文件。如果你不写-m,repo会默认取分支下的default.xml,而default.xml通常是一个重定向文件,它会include最近一次正式发布的版本xml。对于开发环境搭建,我建议每次都用-m锁定,这样不同机器的同步结果一致,排查构建问题时不会出现“同一个版本但源码不同”的尴尬。
3.2 执行repo sync并确认meta层
初始化完成后执行同步。-j是并发任务数,不是越快越好:
repo sync -j4-j4表示最多同时启动4个git fetch任务。在带宽充足时-j8可以缩短总时长,但会更容易被GitHub的并发连接限制盯上而报RPC failed; curl 56 OpenSSL SSL_read。如果同步中断,直接再次执行同一命令即可,repo会跳过已经完成的仓库,不会重复拉取。需要留意的是,repo sync结束后要确认所有仓库都已检出到manifest指定的提交:
repo status | grep -E '^\-\-' | wc -l理论上这个输出是0。如果出现非零行,说明某个仓库没有完全同步,可以针对那个项目单独再跑repo sync sources/meta-imx。
同步完成后检查sources目录下的记录:
ls sources/常见会看到meta-imx、meta-freescale、meta-openembedded等多层名字。其中meta-imx是NXP的官方层,meta-freescale是社区维护的freescale层,meta-openembedded是OE社区层。在imx6相关配置里,实际起作用的是meta-imx和meta-freescale的BSP层,它们通过conf/layer.conf里的BBFILES把下一层的recipes整合进来。如果看到一个meta层但它的依赖层不在sources里,通常是因为manifest没包含它,需要回到manifest里追加,而不是手动往sources里塞。
3.3 初始化构建环境脚本
源码树根目录下会有imx-setup-release.sh脚本,它干的事情比较多:创建build目录、在conf目录生成local.conf、bblayers.conf,并把Yocto的初始化脚本oe-init-build-env的上下文带入当前shell。
source imx-setup-release.sh -b build-imx6 -d fsl-imx-xwayland这里的source不能换成bash去执行,因为脚本里的export命令要在当前shell生效,构建时bitbake才能找到交叉编译工具链的环境变量。-b build-imx6指定在源码树根目录下生成build-imx6作为构建目录;-d fsl-imx-xwayland指定DISTRO。对于不带GPU的imx6ull,fsl-imx-fb更轻量,不引入weston和wayland的依赖;带GPU的imx6dl/qp则建议用fsl-imx-xwayland,它同时提供X11和Wayland的backend,后期测试Qt应用更方便。
脚本执行后,当前目录会切到build-imx6。之后每次新开终端构建,都要回到源码树根目录,执行:
cd ~/imx6-yocto source setup-environment build-imx6setup-environment是上面那个脚本复制到build目录里的,它修改变量让bitbake从当前目录读取conf/local.conf。如果提示找不到setup-environment,可以检查它是否被加上了执行权限,或者直接bash setup-environment build-imx6,但同一条规则:环境变量只对子进程和后续命令有效,必须用source才不会丢失。
4. 配置imx6的local.conf:MACHINE、并行度与缓存路径
4.1 MACHINE怎么对应你的板子
MACHINE变量告诉bitbake要为目标板卡的哪个SoC平台生成设备树和引导配置。imx6系列几个常见值的含义要记清楚:imx6qpsabresd是i.MX6QP的SABRE开发板,imx6qsabresd是i.MX6Q四核,imx6dlsabresd是i.MX6DL双核,imx6ulevk则是i.MX6UL/6ULL的评估板。在build-imx6/conf/local.conf里找到MACHINE这一行并修改:
MACHINE = "imx6ulevk"MACHINE写错时最容易观察到的现象是:bitbake能进入解析阶段,但会提示No recipes available for: ...或者The following machine descriptions are not found,因为meta-imx-bsp/conf/machine目录下找不到对应的*.conf。也有写错后不报错的情况,比如把imx6ulevk写成imx6ulev,最终生成的设备树是imx6ul-14x14-evk.dtb,而板子实际需要的是imx6ull-14x14-evk.dtb,烧进去后控制台无输出。所以修改后先执行一次bitbake -e | grep MACHINE确认环境变量生效。
| MACHINE值 | SoC | 设备树示例 | 常见载体 |
|---|---|---|---|
| imx6qpsabresd | i.MX6QP | imx6qp-sabresd.dtb | SABRE开发板 |
| imx6qsabresd | i.MX6Q | imx6q-sabresd.dtb | 四核板卡 |
| imx6ulevk | i.MX6UL/ULL | imx6ull-14x14-evk.dtb | 评估板、自研板 |
如果你的板子是自研的,正确的做法不是改原厂MACHINE值,而是创建自己的meta层,在layer.conf里设置MACHINE = "myboard",然后在该层的conf/machine/myboard.conf里通过require包含原厂的imx6ulevk.conf,再用KERNEL_DEVICETREE覆写设备树路径。这样原厂升级BSP时,你的板级配置可以通过git合并最小冲突。
4.2 并行度和内存的匹配关系
Yocto的并行分为BitBake任务级和make进程级两层。BB_NUMBER_THREADS控制同时执行多少recipe任务,PARALLEL_MAKE控制每个任务内部make用几个进程。两者都设成CPU核心数,在编译Linux内核或glibc时会在短时间内叠加出几倍的内存请求。我给它们的默认建议是:
BB_NUMBER_THREADS ?= "${@os.cpu_count()}" PARALLEL_MAKE ?= "-j 4"?=的语义是“如果变量尚未被赋值才赋值”,这样如果后续meta层通过local.conf追加了不同值,不会被这里覆盖。os.cpu_count()是Python表达式,bitbake会在解析时读取宿主机的逻辑核心数。如果你的机器是4核8线程,BB_NUMBER_THREADS会是8,但PARALLEL_MAKE固定为4,在内存16GB时刚好;如果内存只有8GB,建议把BB_NUMBER_THREADS也手动改成4并给PARALLEL_MAKE降为-j 2。
一个常见误区是以为-j参数越大构建越快。实际上当CPU占用率达到100%后,再增加任务只会增加内存带宽和进程切换开销。观察构建是否过载的简单方法是开第二个终端跑htop,如果看到多个gcc进程的总内存超过物理内存,立刻bitbake -k结束本次构建,调低后重来。
4.3 DL_DIR和SSTATE_DIR的路径规划
DL_DIR存放所有从网络下载的源码压缩包,SSTATE_DIR存放编译生成的缓存。它们默认落在构建目录里,但如果你隔几天清理一次build目录,缓存的损失很可惜。把它们指向固定路径:
DL_DIR = "/data/yocto/downloads" SSTATE_DIR = "/data/yocto/sstate-cache"两个路径需要在local.conf的注释区之外设置,建议写在文件末尾。DL_DIR的粒度是源码包,完全可以跨MACHINE跨DISTRO复用;SSTATE_DIR则绑定了MACHINE和DISTRO的组合,换一个DISTRO后,新DISTRO的构建仍会把旧缓存当作无效缓存自动跳过。需要说明的是:如果你启用了SSTATE_MIRRORS指向局域网内的sstate服务器,那么SSTATE_DIR本地的优先级更高,镜像只是回退选项。
这里补一个安全且常用的离线构建技巧:主机无法访问外网时,找一台能联网的机器先构建同一个manifest,然后整个拷贝它的downloads目录。Yocto在fetch阶段会先查找DL_DIR里是否存在同名压缩包,存在就跳过网络访问。downloads目录的优点是同一份源码包在多次构建中可以复用,不需要担心任务间的变更。
4.4 构建目标选哪个镜像并开始bitbake
imx6有两个常见的镜像目标:imx-image-core是一个精简的串口控制台镜像,包含busybox、网络工具和基础库,适合验证串口和网络驱动;imx-image-full会在core的基础上加入weston、GStreamer和多媒体编解码组件,体积更大。第一次构建强调验证流程,直接选core即可,避免把GUI依赖的编译时间计入排错周期。
bitbake imx-image-core执行后第一个阶段是解析全部layer,命令行会卡在Loading cache较长时间,这是正常的。日志进入recipe任务阶段后,可以打开tmp/log/cooker/下的log观察执行进度。若想边编译边看log实时输出,另开终端:
tail -f tmp/work/*/*/temp/log.do_compile如果构建中途失败,bitbake会在终端打印最后的错误任务名,比如ERROR: Task (virtual:.../linux-imx_5.15.bb:do_compile) failed。不要盲目重跑,先看对应构建目录temp/log.do_compile的最后几十行,大多数configure: error: C compiler cannot create executables这类报错都能从这里定位到缺少的依赖或错误的MACHINE值。
5. 从部署目录取出imx6镜像并复用sstate加速二次构建
5.1 定位镜像文件并完成烧录验证
构建成功后,所有产物汇聚在tmp/deploy/images/ /。以imx6ulevk为例,你要找的是imx-image-core-imx6ulevk.wic这个SD卡镜像,它既包含u-boot、设备树和kernel,也包含rootfs分区。先用file命令确认格式:
file tmp/deploy/images/imx6ulevk/imx-image-core-imx6ulevk.wic正常输出会识别为DOS/MBR boot sector或filesystem数据,说明镜像头部已经有分区表。接着用lsblk确认SD卡设备名,比如/dev/sdb,然后执行:
sudo dd if=tmp/deploy/images/imx6ulevk/imx-image-core-imx6ulevk.wic of=/dev/sdb bs=1M conv=fsync status=progress sudo syncbs=1M的块大小和conv=fsync保证数据写穿到设备后再返回,避免拔出SD卡时缓存未落盘。烧录后插入板卡,正常情况下串口输出能直接看到U-Boot启动,然后进入内核。这一步才真正验证了MACHINE配置和BSP版本没有跑偏。
5.2 sstate增量复用与rm_work的取舍
二次开发时的效率提升比首次构建更值得关注。修改了kernel配置后,直接在build目录再次bitbake imx-image-core即可,bitbake会以do_kernel_configcheck的任务调度让人以为它要重新编译整个内核,实际它的任务生命周期判断准确到单个recipe。不要习惯性使用bitbake -c cleanall linux-imx,除非你确定要放弃该recipe的所有缓存。
还有一个实用技巧是往local.conf里加INHERIT += "rm_work",它会在每个recipe完成构建后删除工作目录里的临时源码,只保留镜像和日志。这对长期构建能省下接近一半的磁盘,但会失去在tmp/work里手动调试编译问题的机会。如果你更看重调试体验,建议不加,而是定期用bitbake -c clean清理特定组件。
最后一招是给bitbake设置磁盘监控,防止第2章提到的磁盘写满问题再现:
BB_DISKMON_DIRS = "1G;100M;${DL_DIR} 1G;100M;${SSTATE_DIR}"这个变量让bitbake在磁盘剩余低于1G时停止新任务,低于100M时直接abort,避免在do_image_wic阶段才因空间不足而失败。
本文还有配套的精品资源,点击获取