嵌入式Linux内核移植基础:内核源码获取与版本选择、Kconfig与Makefile机制、内核配置(menuconfig)与编译
好,咱们正式开始啃内核移植这块硬骨头。说实话,很多刚入行的朋友一听到「移植」两个字就觉得头大,觉得那是内核大神才干的活。其实不然,你只要把几个关键环节搞明白,这事儿就没那么玄乎。
我个人习惯把内核移植拆成三块:源码怎么拿、配置怎么玩、编译怎么过。今天咱们就把这三块挨个捋一遍。
内核源码获取与版本选择
先说说源码。你可能会问:「不就是去 kernel.org 下载吗?」嗯,理论上没错,但实际项目里没那么简单。
我一般会参考这几个维度来选版本:
| 考量因素 | 说明 | 我的经验 |
|---|---|---|
| 芯片厂商BSP | 厂商提供的板级支持包通常绑定某个内核版本 | 优先用厂商推荐的版本,省掉大量适配工作 |
| LTS长期支持版 | LTS版本维护周期长达6年 | 产品选型我必选LTS,安全补丁不断更 |
| 社区活跃度 | 版本太老,驱动和工具链支持会掉队 | 建议选发布不超过2年的LTS版本 |
| Modbus协议栈兼容性 | 有些内核版本对串口和网络子系统的API有变动 | 我踩过坑,4.19到5.4的tty层改动就挺大 |
获取源码的方式,我常用的就三种:
- git clone 官方仓库:适合需要跟踪最新补丁的情况
- 下载tar包:适合固定版本、不打算频繁更新的项目
- 芯片厂商提供的BSP包:里面已经打好了板级补丁,开箱即用
Kconfig与Makefile机制
内核的配置系统,说白了就是靠 Kconfig 和 Makefile 这两兄弟撑起来的。你想想看,几千个驱动、几百个功能模块,怎么管理?靠的就是这套机制。
Kconfig 负责定义「有什么选项可以选」。每个驱动目录下都有一个 Kconfig 文件,里面写着这个驱动叫什么、依赖什么、有什么依赖关系。
举个例子,咱们Modbus网关里要用到串口驱动,它的Kconfig片段大概长这样:
config SERIAL_8250
tristate "8250/16550 and compatible serial support"
depends on HAS_IOMEM
select SERIAL_CORE
help
This is the driver for the standard serial ports.
If you want to use a serial port, say Y here.
你看,tristate 表示可以编进内核(Y)、编成模块(M)或者不编(N)。depends on 是依赖关系,select 是自动选中其他选项。
Makefile 则负责「怎么编译」。它根据 Kconfig 里生成的配置宏,决定哪些 .c 文件参与编译。
obj-$(CONFIG_SERIAL_8250) += 8250.o
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
这里的 CONFIG_SERIAL_8250 就是配置宏。如果配置成 Y,就编译进内核;配置成 M,就编译成模块;没配置,就不编译。
内核配置(menuconfig)与编译
好,到了实操环节。配置内核,我几乎只用 make menuconfig。为什么?因为它有图形界面,选项一目了然,还能搜索。
基本流程是这样的:
- 清理环境:
make distclean,把之前的配置和编译产物全清掉 - 导入默认配置:
make ARCH=arm defconfig,或者用厂商提供的配置文件 - 个性化配置:
make ARCH=arm menuconfig,按需打开或关闭功能 - 保存配置:退出时自动保存为 .config 文件
- 编译内核:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4
这里我重点说说 menuconfig 里的搜索功能。按 / 键,输入关键词,就能找到所有相关选项。这个功能我几乎天天用,尤其是当你知道某个驱动名字但不知道它在菜单树哪个位置的时候。
编译的时候,有几个参数你必须搞清楚:
| 参数 | 含义 | 说明 |
|---|---|---|
| ARCH | 目标架构 | arm、arm64、x86、mips 等 |
| CROSS_COMPILE | 交叉编译工具链前缀 | 比如 arm-linux-gnueabihf- |
| -jN | 并行编译线程数 | 一般设为 CPU 核心数的 1.5~2 倍 |
| LOADADDR | 内核加载地址 | 某些架构需要指定,比如 U-Boot 引导时 |
编译完成后,你会得到几个关键产物:
- zImage / uImage:压缩后的内核镜像
- vmlinux:未压缩的内核,带调试信息
- arch/arm/boot/dts/xxx.dtb:设备树二进制文件
- modules:编译出的内核模块,分布在各个目录下
下面这张图,是我自己总结的内核配置与编译的完整流程,你可以对照着看:
最后说一句,配置和编译这事儿,熟能生巧。我第一次配内核的时候,对着菜单看了两个小时不知道选什么。后来做多了,闭着眼睛都能把Modbus网关需要的串口、网络、GPIO、SPI这些子系统配出来。
嗯,内核移植的基础就这些。你先把源码拿到手,把 menuconfig 玩熟,把编译流程跑通,后面咱们再聊设备树和驱动移植,就顺理成章了。