1. 嵌入式Linux开发环境搭建
嵌入式Linux开发,说白了就是“交叉编译”。你想想看,我们的开发电脑是x86架构,而目标板通常是ARM、RISC-V或者MIPS。在x86上编译出ARM能跑的程序,这就是交叉编译的核心。
我个人习惯先把整个环境拆成三块:交叉编译工具链、网络服务(TFTP/NFS)、开发板启动流程。这三块搞定了,后面26章的路就好走了。
核心要点: 开发环境搭建不是一次性的。我建议你每换一个项目,都重新验证一遍这三块是否联通。我在项目中遇到过好几次,板子变砖了才发现TFTP目录配错了路径。
1.1 交叉编译工具链安装
工具链的选择,其实就两种:官方预编译版和自己用crosstool-ng编译。对于大多数项目,我建议直接用官方版,省时间。
| 工具链来源 | 适用场景 | 安装方式 |
|---|---|---|
| Linaro GCC | ARM 32位/64位通用开发 | 下载tar.xz,解压到/opt |
| ARM官方gcc-arm-none-eabi | 裸机或RTOS开发 | apt install / 手动解压 |
| Buildroot / Yocto SDK | 完整系统构建后配套 | 跟随构建系统生成 |
| crosstool-ng自编译 | 特殊架构或定制glibc版本 | ct-ng build |
安装步骤其实很简单。以Linaro GCC 7.5为例:
# 下载工具链
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 解压到/opt
sudo tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt/
# 添加环境变量
export PATH=/opt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin:$PATH
# 验证安装
arm-linux-gnueabihf-gcc --version
我的习惯: 把工具链路径写到 ~/.bashrc 或 /etc/profile.d/ 里,这样每次开终端就不用重新export了。另外,我建议用软链接管理多版本工具链,比如 /opt/arm-gcc -> /opt/gcc-linaro-7.5.0/。
1.2 TFTP/NFS服务器配置
这两个服务是嵌入式开发的“生命线”。TFTP用来下载内核和设备树,NFS用来挂载根文件系统。没有它们,你每次调试都得烧写Flash,效率太低了。
1.2.1 TFTP服务器
TFTP说白了就是一个极简的文件传输协议,没有认证,没有目录浏览,就是“你给我文件名,我给你文件”。
# 安装TFTP服务器(Ubuntu/Debian)
sudo apt install tftpd-hpa
# 配置文件 /etc/default/tftpd-hpa
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/srv/tftp" # 这就是你的文件存放目录
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="--secure --create"
# 创建目录并设置权限
sudo mkdir -p /srv/tftp
sudo chmod 777 /srv/tftp
# 重启服务
sudo systemctl restart tftpd-hpa
我曾经踩过的坑: TFTP默认端口69需要root权限。如果你用非root用户启动服务,记得检查防火墙。还有,TFTP文件大小限制通常为32MB,内核太大的话会传输失败。解决办法是用 --blocksize 1468 选项加大块大小。
1.2.2 NFS服务器
NFS比TFTP复杂一点,但好处是开发板的根文件系统可以直接跑在主机上。改代码、编译、测试,一气呵成。
# 安装NFS服务器
sudo apt install nfs-kernel-server
# 编辑 /etc/exports,添加共享目录
/srv/nfs/rootfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
# 创建根文件系统目录
sudo mkdir -p /srv/nfs/rootfs
# 把编译好的根文件系统解压到这里
# 重启NFS服务
sudo systemctl restart nfs-kernel-server
# 验证挂载(在开发板u-boot或内核命令行中)
# root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs,vers=3
关键参数说明:
no_root_squash:允许开发板以root身份访问文件,调试时必备vers=3:使用NFSv3,兼容性最好。NFSv4在某些老内核上会有问题rw:读写权限,方便在板子上直接修改脚本
1.3 开发板启动流程
搞嵌入式Linux,你必须理解板子从上电到进入shell的完整过程。我见过太多人只会烧录,出了问题就抓瞎。
启动流程其实就四步:
- BootROM:芯片内部固件,初始化时钟、DDR,从启动介质加载SPL
- SPL (Secondary Program Loader):初始化DDR和存储控制器,加载u-boot
- u-boot:完整的bootloader,加载内核和设备树到内存,传递启动参数
- Kernel:解压、初始化硬件、挂载根文件系统、启动init进程
1.3.1 u-boot常用命令
u-boot是调试阶段打交道最多的组件。我建议你记住这几个命令:
| 命令 | 作用 | 示例 |
|---|---|---|
printenv |
打印所有环境变量 | 查看bootargs、serverip |
setenv |
设置环境变量 | setenv bootargs console=ttyS0,115200 |
tftp |
通过TFTP下载文件 | tftp 0x42000000 zImage |
bootm |
启动内核 | bootm 0x42000000 - 0x43000000 |
nfs |
通过NFS挂载根文件系统 | nfs 0x42000000 serverip:/path |
1.3.2 典型启动参数示例
# 从TFTP下载内核,NFS挂载根文件系统
setenv bootargs 'console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.1.100:/srv/nfs/rootfs,vers=3 rw ip=192.168.1.50:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'
# 从MMC启动
setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait rw'
# 保存环境变量
saveenv
# 启动
tftp 0x42000000 zImage
tftp 0x43000000 imx6ull-14x14-evk.dtb
bootm 0x42000000 - 0x43000000
我曾经踩过的坑: 第一次用NFS启动时,板子一直卡在“VFS: Unable to mount root fs”。查了半天,原来是NFS服务器上的根文件系统权限没设置对。记得检查 /etc/exports 里的 no_root_squash 选项,还有根文件系统里 /sbin/init 是否存在且可执行。
1.4 环境验证清单
搭建完环境后,我建议你按这个清单逐项验证:
- 工具链验证: 编译一个hello world,用
file命令确认是ARM架构 - TFTP验证: 在开发板u-boot中执行
tftp 0x42000000 test.txt,确认能下载 - NFS验证: 在开发板u-boot中执行
nfs 0x42000000 serverip:/srv/nfs/rootfs/test.txt - 完整启动验证: 用TFTP下载内核和DTB,用NFS挂载根文件系统,直到进入shell
我的建议: 第一次启动成功后,把u-boot的环境变量用 printenv 保存下来。以后换板子或者重刷固件,直接照着配就行,省得每次都要查文档。
嗯,环境搭建这部分就到这里。记住一句话:环境搭得好,调试没烦恼。后面我们就要开始真正的内核配置和设备树实战了。