第4章:Linux内核移植基础——获取源码、配置与ARM架构裁剪编译
内核移植,说白了就是让Linux内核跑在你手上的那块ARM板子上。很多人一上来就急着改代码,结果连内核怎么配置、怎么裁剪都没搞清楚。我刚开始做移植时也犯过这毛病,拿着个默认配置就编译,烧进去发现串口没输出,折腾了两天才发现是内核里根本没开对应UART驱动。
这一章,咱们把基础打牢。从获取源码开始,到配置、裁剪、编译,最后生成zImage或Image。每一步我都会把项目里踩过的坑指给你看。
本章核心目标:掌握Linux内核源码获取、menuconfig配置、ARM架构裁剪、编译生成zImage/Image的完整流程。
4.1 获取Linux内核源码
获取源码的方式有好几种,我按推荐顺序列出来:
| 方式 | 命令/地址 | 适用场景 |
|---|---|---|
| kernel.org 官方主线 | https://www.kernel.org/ |
学习、新项目起步 |
| git clone 主线仓库 | git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git |
需要跟踪最新提交 |
| SoC厂商提供BSP包 | 如NXP、TI、Rockchip官网 | 量产项目、有硬件支持 |
| LTS长期支持版 | kernel.org 上标记LTS的版本 | 产品稳定期、维护项目 |
我个人习惯用git clone主线仓库。为什么?因为方便切换分支、打补丁、回退版本。你想想看,如果从官网下载tar包,想换个版本还得重新下载,多麻烦。
小技巧:国内网络访问kernel.org慢的话,可以用清华、中科大的镜像源。比如:git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git
4.2 配置内核——make menuconfig
拿到源码后,第一步不是直接编译,而是配置。配置内核就像搭积木,你需要决定哪些功能要、哪些不要。
配置前,先看看你的ARM平台有没有现成的默认配置:
# 查看所有ARM相关默认配置
ls arch/arm/configs/
# 以vexpress开发板为例
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
# 然后打开menuconfig做微调
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
menuconfig界面里,选项有三种状态:
- [*] —— 编译进内核(built-in)
- [M] —— 编译成模块(module)
- [ ] —— 不编译
嗯,这里要注意:驱动移植阶段,我建议先把关键驱动编进内核,别用模块。为什么?因为模块需要文件系统加载,如果文件系统还没起来,驱动就加载不了,板子直接哑火。我在项目里吃过这个亏,那次串口驱动配成了模块,结果内核启动后根本没法交互,最后只能重新烧固件。
避坑指南:我曾经在配置网络驱动时,把PHY芯片驱动漏了,结果网卡能识别但死活ping不通。排查了两天才发现是PHY驱动没选上。所以配置完一定要检查依赖关系,用make ARCH=arm menuconfig里的"Show dependencies"功能。
4.3 ARM架构内核裁剪策略
裁剪不是乱删,得有策略。我总结了一套"三步裁剪法":
- 平台无关裁剪:关掉不需要的文件系统(如NTFS、XFS)、网络协议(如IPX、DECnet)、虚拟化功能
- ARM架构裁剪:只保留目标CPU型号,关掉其他ARM核的支持
- 板级裁剪:只保留板子上实际用到的外设驱动
下面这张图展示了裁剪的层次关系:
具体到ARM架构,重点裁剪这几个地方:
| 配置路径 | 裁剪内容 | 节省空间 |
|---|---|---|
System Type → ARM system type |
只保留目标CPU,如Cortex-A7 | ~2MB |
Kernel Features |
关掉SMP(单核)、关掉PREEMPT(非实时) | ~500KB |
Device Drivers |
只保留I2C、SPI、GPIO、UART等实际外设 | ~5MB |
File systems |
只保留ext4、tmpfs、proc、sysfs | ~3MB |
经验之谈:裁剪后记得用make ARCH=arm savedefconfig生成最小配置文件。下次编译直接加载这个文件,省得每次都要重新选。我每个项目都会保留一份defconfig,方便团队复用。
4.4 编译生成zImage/Image
配置搞定后,编译就简单了。但有几个参数必须搞清楚:
# 编译生成Image(未压缩)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 Image
# 编译生成zImage(压缩后的镜像)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage
# 编译生成uImage(带uboot头的镜像)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 uImage
这三种镜像有什么区别?我整理了个对比表:
| 镜像类型 | 压缩方式 | 典型大小 | 启动方式 |
|---|---|---|---|
| Image | 未压缩 | ~15MB | 直接加载到内存运行 |
| zImage | gzip压缩 | ~5MB | 自解压,适合uboot引导 |
| uImage | zImage + uboot头 | ~5.1MB | uboot专用,带校验头 |
实际项目中,我大部分时候用zImage。为什么?因为uboot对zImage支持最好,而且压缩后体积小,烧录快。但要注意一点:如果你的板子内存很小(比如64MB以下),zImage自解压时会占用额外内存,这时候用Image更稳妥。
注意:编译时-j后面的数字别太大。我见过有人用-j32编译,结果内存爆了直接OOM。一般-j4或-j8就够了,除非你服务器配置特别高。
4.5 编译后的验证与常见问题
编译完成后,先别急着烧录。做两个快速验证:
- 检查镜像大小:
ls -lh arch/arm/boot/zImage,如果比预期大很多,说明裁剪没到位 - 查看内核版本:
strings arch/arm/boot/zImage | grep "Linux version",确认版本号正确
我遇到过最坑的问题,是编译出来的zImage烧进去后,uboot报"Wrong Image Format"。排查了半天,发现是交叉编译工具链版本不对。ARMv7的核用了ARMv5的工具链,指令集不兼容。
检查工具链:
# 查看工具链支持的架构
arm-linux-gnueabihf-gcc -v 2>&1 | grep --color=auto "Target"
# 查看内核实际使用的指令集
head -5 arch/arm/Makefile | grep "ARCH_TEXT"
另一个常见问题是配置了设备树但没编译进去。记得检查:
# 确认设备树是否被编译
ls -lh arch/arm/boot/dts/你的板子.dtb
# 如果没生成,检查配置
make ARCH=arm menuconfig
# 进入 Device Drivers → Device Tree and Open Firmware support
# 确保 CONFIG_OF 和 CONFIG_OF_FLATTREE 是 [*]
嗯,到这里,内核的获取、配置、裁剪、编译流程就走通了。你手头如果有ARM开发板,现在就可以动手试试。从make menuconfig开始,一步步裁剪,看看最终生成的zImage能小到什么程度。我做过一个极限裁剪实验,把Cortex-A7的内核裁到了2.3MB,启动时间从8秒降到了1.2秒——当然,那是把能关的全关了,只留了串口和GPIO。