2. 嵌入式系统开发环境搭建:交叉编译工具链、嵌入式Linux内核配置、根文件系统制作
好,咱们正式开始动手。这一章是嵌入式开发的“入场券”——环境搭不起来,后面全是纸上谈兵。我见过不少新手,拿到开发板第一件事就是插电看灯闪,结果一编译就报错,卡好几天。说白了,嵌入式开发环境和你在PC上写个Hello World完全是两码事。
2.1 交叉编译工具链:为什么你不能在开发板上编译?
你想想看,你的开发板可能是个ARM Cortex-A72,主频1.5GHz,内存2GB。在上面跑个GCC编译Linux内核?那得等到明天。更现实的问题是——很多嵌入式设备根本没有完整的操作系统,或者存储空间连编译器都装不下。
所以我们的做法是:在PC(x86架构)上编译,在目标板(ARM/RISC-V等)上运行。这就是交叉编译。
核心概念:交叉编译工具链 = 一套运行在Host(PC)上、生成Target(开发板)可执行代码的编译器、链接器、库文件集合。
2.1.1 工具链的三大件
| 组件 | 典型名称 | 作用 |
|---|---|---|
| 编译器 | arm-linux-gnueabihf-gcc | 将C/C++源码编译为目标架构的汇编/机器码 |
| 链接器 | arm-linux-gnueabihf-ld | 将目标文件链接成可执行文件或共享库 |
| 调试器 | arm-linux-gnueabihf-gdb | 在PC上远程调试开发板上的程序 |
我个人习惯用 arm-linux-gnueabihf- 这个前缀。看到这个前缀,你就知道它是给ARM硬浮点架构用的。如果是软浮点,那就是 arm-linux-gnueabi-。别搞混了,我踩过这个坑——编译出来的程序一运行就报非法指令。
2.1.2 获取工具链的三种方式
- 从芯片厂商获取:最省心。比如NXP的Linaro工具链、Xilinx的PetaLinux自带工具链。
- 使用Buildroot/Yocto自动构建:适合定制化需求,但第一次编译可能要几个小时。
- 手动从Linaro官网下载:我推荐这个。直接下载预编译的二进制包,解压就能用。
我的习惯:把工具链解压到 /opt/toolchains/ 目录下,然后在 ~/.bashrc 里加一行 export PATH=$PATH:/opt/toolchains/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin。这样每次打开终端就能直接用。
验证工具链是否装好,跑这条命令:
arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (GCC) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
...
看到版本号,说明工具链活了。
2.2 嵌入式Linux内核配置:别被上万选项吓到
我第一次配内核时,打开menuconfig,看到密密麻麻的选项,头皮发麻。其实没那么可怕——你只需要关注跟你的硬件相关的部分。
2.2.1 获取内核源码
从kernel.org下载官方主线内核,或者从芯片厂商的Git仓库拉取。我建议用厂商提供的版本,因为里面已经包含了板级支持包(BSP)。
# 以Linux 5.10 LTS为例
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.100.tar.xz
tar -xf linux-5.10.100.tar.xz
cd linux-5.10.100
2.2.2 配置内核的三种方法
| 方法 | 命令 | 适用场景 |
|---|---|---|
| 基于默认配置 | make ARCH=arm defconfig |
快速生成通用配置,再手动微调 |
| 基于厂商配置 | make ARCH=arm imx_v7_defconfig |
厂商已经配好了大部分选项 |
| 完全手动 | make ARCH=arm menuconfig |
从零开始,适合深度定制 |
我一般用第二种。比如给NXP i.MX6ULL开发板配内核:
make ARCH=arm imx_v7_defconfig
make ARCH=arm menuconfig
注意:menuconfig里按 / 键可以搜索选项。我曾经为了找一个驱动,在菜单里翻了十分钟,后来才知道有搜索功能。输入 CONFIG_TOUCHSCREEN,直接定位。
2.2.3 必须配置的几个关键项
- 处理器类型:选对你的CPU型号,比如ARM Cortex-A7。
- 设备驱动:网卡、I2C、SPI、LCD、摄像头接口——用到的才选,用不到的统统去掉。内核越小,启动越快。
- 文件系统支持:如果你用ext4,就把ext4编进内核;如果用initramfs,要配置对应的选项。
- 网络协议栈:IPv4基本必选,IPv6看需求。
配置完保存,然后编译:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 dtbs
编译出来的 zImage 和 .dtb 文件,就是你的内核和设备树。
2.3 根文件系统制作:让Linux“活”起来
内核启动后,需要挂载根文件系统。没有它,系统就卡在 Kernel panic - not syncing: VFS: Unable to mount root fs。嗯,这个错误我见过太多次了。
2.3.1 根文件系统里有什么?
| 目录 | 内容 |
|---|---|
/bin |
基本用户命令(ls, cp, cat等) |
/sbin |
系统管理命令(init, mount, ifconfig等) |
/dev |
设备节点(由devtmpfs或mdev自动管理) |
/etc |
配置文件(inittab, fstab, passwd等) |
/lib |
动态库和内核模块 |
/usr |
用户程序和数据 |
/var |
可变数据(日志、临时文件) |
2.3.2 制作根文件系统的两种主流方法
方法一:使用BusyBox(轻量级,适合资源受限设备)
BusyBox是一个“瑞士军刀”——一个二进制文件集成了上百个Linux命令。我最早做产品原型时就用它,从配置到生成根文件系统,半小时搞定。
# 下载并配置BusyBox
wget https://busybox.net/downloads/busybox-1.33.1.tar.bz2
tar -xf busybox-1.33.1.tar.bz2
cd busybox-1.33.1
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
# 记得选上 "Build BusyBox as a static binary (no shared libs)"
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- install CONFIG_PREFIX=../rootfs
然后手动创建必要的目录和设备节点:
cd ../rootfs
mkdir -p proc sys tmp dev etc/init.d
# 创建基本的设备节点
sudo mknod -m 666 dev/null c 1 3
sudo mknod -m 666 dev/console c 5 1
方法二:使用Buildroot(自动化,适合产品级开发)
Buildroot能自动下载、配置、编译工具链、内核、根文件系统,一条龙服务。我现在的项目基本都用它。
# 下载Buildroot
git clone https://git.buildroot.net/buildroot
cd buildroot
make menuconfig
# 选择Target选项:ARM架构、具体CPU型号
# 选择Toolchain:可以用外部工具链,也可以让Buildroot自己编译
# 选择System configuration:主机名、密码、启动方式
# 选择Filesystem images:ext4、squashfs、initramfs等
make -j4
编译完成后,在 output/images/ 目录下会生成 rootfs.ext4、rootfs.tar 等文件。
避坑指南:我曾经用Buildroot编译一个带Qt5和OpenCV的项目,第一次编译花了6个小时。后来发现可以配置 BR2_CCACHE=y 启用编译缓存,第二次编译就快多了。
2.3.3 根文件系统的三种部署方式
| 方式 | 存储位置 | 优点 | 缺点 |
|---|---|---|---|
| initramfs | 与内核打包在一起 | 无需额外存储设备,启动极快 | 占用内存,修改不方便 |
| SD卡/eMMC分区 | ext4分区 | 容量大,可读写,方便调试 | 需要分区和烧录步骤 |
| NFS网络挂载 | PC上的目录 | 开发阶段修改代码立竿见影 | 依赖网络,不适合产品 |
我个人在开发阶段最喜欢用NFS。在PC上修改一个脚本,开发板上重启服务就能看到效果,不用反复烧写SD卡。
2.4 本章知识体系总览
下面这张图把三者的关系和流程串起来了。你照着这个顺序做,环境搭建不会乱。
总结一句话:工具链是“笔”,内核是“骨架”,根文件系统是“血肉”。三者配合好,你的嵌入式Linux系统才能跑起来。