4. 内核配置与裁剪:让Linux内核轻装上阵
嵌入式系统资源有限,Flash和RAM都是按MB甚至KB来算的。一个完整的内核动辄几十MB,直接烧进去?不现实。所以,内核配置与裁剪是每个嵌入式Linux工程师的必修课。说白了,就是去掉你不需要的,只留下硬件必须的。
我个人习惯,拿到一块新板子,第一件事不是写驱动,而是先做内核裁剪。把基础跑通了,再慢慢加功能。这章我们就聊聊怎么把内核“瘦身”成功。
4.1 Kconfig机制:内核配置的“语法书”
Kconfig是啥?它是内核配置系统的“语法”。每个驱动、每个子系统,都有一个Kconfig文件,定义了它的开关、依赖和帮助信息。你运行make menuconfig时看到的界面,就是Kconfig解析出来的。
一个典型的Kconfig条目长这样:
config CONFIG_DRIVER_NAME
tristate "My Driver Support"
depends on ARM
select CONFIG_ANOTHER_DRIVER
help
This driver supports my custom hardware.
Say Y here if you need this driver.
这里有几个关键点:
- tristate:三态,Y(编译进内核)、M(编译为模块)、N(不编译)。嵌入式里我建议能用Y就用Y,模块加载有时会引入延迟。
- depends on:依赖关系。比如你的驱动依赖ARM架构,那x86上就看不到这个选项。
- select:反向依赖。选中这个选项,会自动选中另一个。小心循环依赖,我踩过这个坑。
make savedefconfig可以生成最小配置。它会只保留你修改过的选项,去掉所有默认值。这个文件特别适合版本管理。
4.2 基于硬件平台的模块化裁剪
裁剪不是瞎删。你得知道硬件上有什么,没什么。我见过有人把串口驱动删了,结果控制台没了,板子变砖。嗯,这里要注意:先列硬件清单,再动手裁剪。
我的做法是三步走:
- 列出硬件外设:CPU型号、内存大小、存储类型、网卡、串口、I2C、SPI、USB、GPIO等。
- 对照内核选项:每个外设对应哪些驱动,找到Kconfig里的位置。
- 关闭无关项:比如你的板子没有WiFi,那把无线网络子系统整个关掉。
下面这张图展示了裁剪的核心逻辑:
4.3 内核尺寸优化:去掉无用驱动和子系统
裁剪的核心目标就是减小内核镜像体积。我做过一个项目,裁剪前内核是8MB,裁剪后只有1.2MB。怎么做到的?一个字:删。
下面这张表列出了常见的可裁剪子系统:
| 子系统 | 典型尺寸 | 裁剪条件 | 备注 |
|---|---|---|---|
| 无线网络 (CFG80211/MAC80211) | ~500KB | 无WiFi/蓝牙模块 | 直接关闭 |
| USB子系统 | ~300KB | 无USB接口 | 保留HID可能有用 |
| 音频子系统 (ALSA) | ~400KB | 无音频编解码器 | 可模块化 |
| 视频/V4L2 | ~350KB | 无摄像头/显示 | 直接关闭 |
| 文件系统 (EXT4/XFS/BTRFS) | ~200KB/个 | 只用一种文件系统 | 保留需要的即可 |
| 网络协议栈 (IPv6/IPX) | ~150KB | 只用IPv4 | 关闭IPv6 |
| 调试选项 (DEBUG_FS/PRINTK) | ~100KB | 生产环境 | 必须关闭 |
4.4 内核编译优化:LTO和优化等级
裁剪完配置,接下来就是编译优化。这里有两个关键点:优化等级和LTO(链接时优化)。
4.4.1 优化等级的选择
内核默认用-O2。你可以改成-Os(优化尺寸)或-O3(优化性能)。怎么改?在Makefile里找到KBUILD_CFLAGS,或者直接在menuconfig里设置:
# 在menuconfig中
General setup →
Compiler optimization level (Optimize for size (-Os))
我个人建议:生产环境用-Os。为什么?因为嵌入式设备对尺寸敏感,-Os能减少10%-20%的代码体积。代价是性能略有下降,但通常感觉不到。
三种优化等级的对比:
| 优化等级 | 内核尺寸 | 性能 | 适用场景 |
|---|---|---|---|
| -O2 | 基准 | 基准 | 默认,开发调试 |
| -Os | 减少10-20% | 略降1-3% | 生产环境,Flash紧张 |
| -O3 | 增加5-10% | 提升3-5% | 性能敏感,Flash充足 |
4.4.2 LTO:链接时优化
LTO是GCC的一个特性,它能在链接阶段做全局优化。说白了,就是让编译器看到整个程序,而不是单个文件。这样能发现更多优化机会。
启用LTO的方法:
# 在menuconfig中
General setup →
Enable Link Time Optimization (LTO) (EXPERIMENTAL)
启用后,编译时间会变长(大约1.5-2倍),但内核尺寸能再减小5-10%。我试过一个项目,LTO后内核从1.2MB降到了1.1MB,虽然不多,但聊胜于无。
4.5 实战:一个完整的裁剪案例
假设我们有一个ARM Cortex-A7的板子,256MB RAM,128MB NAND Flash,只有串口、网口、I2C和GPIO。没有WiFi、没有USB、没有音频。
裁剪步骤:
- 关闭无线网络:
Networking support → Wireless → [ ] - 关闭USB:
Device Drivers → USB support → [ ] - 关闭音频:
Device Drivers → Sound card support → [ ] - 关闭文件系统:只保留
EXT4和proc、sysfs,其他全关。 - 关闭调试:
Kernel hacking → [ ] Debug Filesystem,[ ] Kernel debugging - 设置优化等级:
-Os - 启用LTO:如果GCC版本支持。
裁剪前后对比:
| 指标 | 裁剪前 | 裁剪后 | 优化比例 |
|---|---|---|---|
| 内核镜像尺寸 | 6.8 MB | 1.5 MB | 78% |
| 编译时间 | 12分钟 | 8分钟 | 33% |
| 启动时间 | 8秒 | 3秒 | 62.5% |
| 内存占用 | 64 MB | 32 MB | 50% |
你看,裁剪的效果非常明显。内核小了,启动快了,内存也省了。这就是嵌入式Linux优化的魅力。