第四章:Linux内核编译与裁剪——为ADAS域控量身定制
各位同学,今天我们来聊聊内核编译与裁剪。说实话,这可能是整个课程里最“硬核”的部分之一。我在ADAS域控项目里,至少亲手编译过上百次内核。为什么?因为车载系统对实时性、安全性和资源占用要求极高,一个“大而全”的通用内核,根本跑不动。
你想想看,ADAS域控的算力是有限的。摄像头数据、雷达点云、决策规划,哪个不吃资源?内核本身如果太臃肿,光调度开销就能吃掉10%的CPU。所以,裁剪不是锦上添花,是必须做的事。
4.1 内核源码获取:从哪开始?
获取源码,我建议直接从kernel.org拉取LTS版本。为什么?因为车载产品生命周期长,LTS版本有长期维护,安全补丁跟得上。
# 以5.10 LTS为例
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.210.tar.xz
tar -xf linux-5.10.210.tar.xz
cd linux-5.10.210
嗯,这里要注意:别用最新的主线版本。我在一个项目里吃过亏,用了当时最新的5.15,结果某个GPU驱动不兼容,调试了整整三天。后来老老实实切回5.10 LTS,问题全没了。
4.2 内核配置(menuconfig):别怕,一步步来
配置内核,最直观的方式就是 make menuconfig。它基于ncurses,在终端里就能操作。我第一次用的时候,看着满屏的选项也懵。但后来发现,其实就三类东西:
- Y:编译进内核(built-in)
- M:编译成模块(module)
- N:不编译
对于ADAS域控,我的原则是:能M不Y,能N不M。为什么?因为模块可以按需加载,减少内核镜像体积,也降低启动时间。
举个例子,摄像头驱动。如果你用Y编译进内核,那每次启动都会加载。但ADAS系统里,摄像头初始化是有时序的,可能需要在应用层控制加载时机。这时候用M就灵活多了。
# 进入配置界面
make menuconfig
# 常用快捷键
# / 搜索某个配置项
# ? 查看帮助
# Space 切换 Y/M/N
避坑指南: 我曾经在配置时,不小心把EXT4文件系统支持设成了M。结果根文件系统在EXT4分区上,内核启动时根本挂载不了,直接panic。后来我学乖了:根文件系统所在分区的驱动,必须设为Y。
4.3 内核编译与模块管理
配置好了,就该编译了。我一般用多核编译,省时间。
# 编译内核镜像
make -j$(nproc) Image
# 编译模块
make -j$(nproc) modules
# 安装模块到目标目录
make modules_install INSTALL_MOD_PATH=/path/to/rootfs
这里有个细节:make -j后面的数字,不是越大越好。我试过在8核机器上开16线程,结果内存爆了,编译到一半卡死。建议用 nproc 获取核心数,或者稍微减1。
编译完成后,你会得到几个关键文件:
| 文件 | 说明 |
|---|---|
| arch/arm64/boot/Image | 内核镜像,用于启动 |
| arch/arm64/boot/dts/xxx.dtb | 设备树文件,描述硬件 |
| System.map | 符号表,调试用 |
| .config | 当前配置,备份用 |
模块管理也很重要。我习惯把模块打包成一个压缩包,部署时直接解压到目标板。
# 打包模块
cd /path/to/rootfs/lib/modules/
tar -czf modules.tar.gz 5.10.210/
# 在目标板上解压
tar -xzf modules.tar.gz -C /lib/modules/
depmod -a
4.4 针对ADAS的内核裁剪:砍掉不需要的
好了,重头戏来了。ADAS域控需要什么?我来列个清单:
- 必须保留: 实时抢占(PREEMPT_RT)、内存管理(CMA)、DMA-BUF、V4L2、GPU/ISP驱动、网络协议栈(用于CAN/Ethernet)
- 可以砍掉: 蓝牙、Wi-Fi(除非需要)、USB Gadget、音频子系统(除非有语音交互)、大量文件系统(只留EXT4和F2FS就够了)
- 必须砍掉: 所有非必要的硬件驱动(比如触摸屏、键盘、鼠标)、虚拟化支持(KVM)、大量网络协议(IPX、AppleTalk等)
我举个例子。有一次,我负责的域控项目,内核镜像从12MB裁剪到了4.8MB。怎么做到的?
- 关掉了所有调试选项(CONFIG_DEBUG_KERNEL=n)
- 只保留ARM64架构,砍掉ARM32支持
- 文件系统只留EXT4和F2FS
- 网络只留IPv4和IPv6,其他全关
- 驱动只保留I2C、SPI、GPIO、PCIe、GPU、ISP、以太网
# 查看当前内核大小
ls -lh arch/arm64/boot/Image
# 裁剪后对比
# 裁剪前:12M
# 裁剪后:4.8M
你想想看,少了7MB,对于启动时间来说,就是几百毫秒的差距。在ADAS里,这几百毫秒可能意味着能不能及时响应一个紧急刹车。
我的经验: 裁剪不是一次完成的。我通常先做一个最小配置,能启动系统。然后逐步添加需要的功能,每加一个就编译测试一次。这样出了问题,很容易定位是哪个配置项导致的。
最后,别忘了保存你的配置文件。我习惯把最终的.config重命名为 defconfig_adas,放到arch/arm64/configs/下。这样下次直接 make defconfig_adas 就能复用。
嗯,内核编译与裁剪,说白了就是“做减法”。去掉你不需要的,留下最核心的。对于ADAS域控来说,稳定、高效、安全,比什么都重要。下一章,我们会聊聊设备树和驱动开发,到时候你会更理解为什么裁剪这么关键。