4、定制Linux内核:内核配置入口、裁剪内核驱动、添加必要的硬件支持(SPI、I2C、UART)、内核模块管理
好,咱们进入第四章节。说实话,内核配置这块,是很多初学者最头疼的地方。菜单项密密麻麻,选项多到让人眼花。但别怕,我带你捋一遍,你会发现其实就那么几件事。
4.1 内核配置入口:从哪里开始?
Buildroot 里配置内核,入口其实就一个命令。你进到 Buildroot 顶层目录,执行:
make linux-menuconfig
这就打开了内核的配置界面。嗯,跟 Buildroot 本身的 make menuconfig 长得有点像,但内容完全不同。这里全是 Linux 内核的选项。
我个人习惯,第一次配置时,先跑一遍默认配置。怎么跑?
make linux-defconfig
这会加载你当前架构的默认内核配置。比如你选的 ARM 架构,它就会加载 arch/arm/configs/ 下的某个 defconfig 文件。有了这个基础,你再去做裁剪,心里就有底了。
output/build/linux-xxx/.config 看一眼。文件头部会写明来源。
4.2 裁剪内核驱动:做减法比做加法难
很多人的误区是「功能越多越好」。其实不是。嵌入式设备资源有限,你塞一堆用不上的驱动,不仅编译慢,镜像还大。更关键的是,驱动多了,内核启动时加载的 init 过程也会变慢。
我做过一个项目,客户要求启动时间控制在 3 秒以内。我第一件事就是砍驱动。怎么砍?
- 关掉所有不用的文件系统支持:比如你只用 ext4,那就把 ext2、ext3、btrfs、xfs 全关掉。
- 关掉不用的网络协议:比如你的设备只用 TCP/IP,那 IPX、AppleTalk、DECnet 这些古董协议就别留了。
- 关掉不用的硬件驱动:比如你的板子没有 SATA 接口,那 SATA 驱动就关掉。
我曾经遇到过一个坑:关掉某个驱动后,系统启动时内核 panic 了。查了半天,发现是内核里有个依赖没注意到。所以,裁剪时别太激进,每关一个选项,最好编译一次,验证一下。
/ 键搜索依赖关系,看清楚再动手。
4.3 添加必要的硬件支持:SPI、I2C、UART
网关设备,说白了就是跟各种传感器、外设打交道。SPI、I2C、UART 这三个总线,几乎是标配。下面我一个个说。
4.3.1 SPI 支持
SPI 常用于高速数据传输,比如 LCD 屏、SD 卡、RF 模块。在内核里,你需要开启:
Device Drivers --->
[*] SPI support --->
<*> SPI controller driver for your SoC
<M> User mode SPI device driver support
注意那个「User mode SPI device driver support」。如果你想让用户空间程序直接操作 SPI,这个要选上。我一般选 <M>,编译成模块,按需加载。
4.3.2 I2C 支持
I2C 是低速总线,常用于传感器、RTC 时钟、EEPROM。配置路径:
Device Drivers --->
[*] I2C support --->
<*> I2C device interface
<*> I2C bus driver for your SoC
这里有个小细节:I2C 的时序参数。有些传感器对时序很敏感,比如温湿度传感器 SHT30。我遇到过因为 I2C 时钟频率太高,导致通信失败的情况。解决办法是在设备树里调整 clock-frequency,一般设成 100kHz 比较稳妥。
4.3.3 UART 支持
UART 是串口,调试、通信都靠它。配置路径:
Device Drivers --->
[*] Character devices --->
[*] Serial drivers --->
<*> Your SoC UART support
[*] Support for console on UART
记得把「Support for console on UART」勾上。否则你连串口终端都看不到。我刚开始做嵌入式时,就犯过这个错——编译完烧进去,串口没输出,还以为板子坏了。
<*>),而不是模块。因为它们是系统基础功能,启动时就需要。
4.4 内核模块管理:什么时候用模块?
内核模块,说白了就是可以动态加载的驱动。好处是灵活,不用了可以卸载,节省内存。坏处是管理起来麻烦,你得确保根文件系统里有对应的 .ko 文件。
我一般遵循这个原则:
- 核心驱动(SPI、I2C、UART、GPIO、中断控制器)→ 编译进内核
- 外设驱动(Wi-Fi 模块、蓝牙、USB 设备)→ 编译成模块
- 调试驱动(比如某些测试工具)→ 编译成模块,调试完就删掉
模块怎么管理?Buildroot 会自动把模块打包到根文件系统里。你只需要在配置内核时,把对应的驱动选成 <M> 就行。系统启动后,用 modprobe 加载:
modprobe spi-dev
modprobe i2c-dev
如果你想开机自动加载,可以把模块名加到 /etc/modules 文件里。Buildroot 的 BR2_ROOTFS_OVERLAY 可以帮你做这件事。
4.5 实战:一个典型的网关内核配置清单
下面是我做的一个物联网网关项目的内核配置清单,供你参考:
| 功能 | 配置选项 | 推荐设置 |
|---|---|---|
| SPI 控制器 | CONFIG_SPI_IMX | 编译进内核 |
| I2C 控制器 | CONFIG_I2C_IMX | 编译进内核 |
| UART 串口 | CONFIG_SERIAL_IMX | 编译进内核 |
| Wi-Fi 模块 | CONFIG_MT76x0U | 编译成模块 |
| USB 存储 | CONFIG_USB_STORAGE | 编译成模块 |
| 文件系统 | CONFIG_EXT4_FS | 编译进内核 |
这个清单不是死的。你根据自己板子的 SoC 型号,找到对应的驱动选项就行。比如你的 SoC 是 Allwinner 的,那就找 CONFIG_SPI_SUNXI、CONFIG_I2C_SUNXI 之类的。
好了,这一章就到这里。内核配置说白了就是「加加减减」——加上你需要的,砍掉你不需要的。多试几次,你就能找到感觉。下一章,咱们聊聊根文件系统的构建,那又是另一番天地。