3. 开发环境搭建
做嵌入式Linux裁剪,第一步就是拿到内核源码。这事听起来简单,但里面坑不少。我见过不少新手直接从网盘下载所谓「优化版」内核,结果后面调试时连问题出在哪都找不到。今天咱们就聊聊怎么正规地获取源码,以及怎么用Kconfig/Makefile这套工具把配置玩明白。
从kernel.org获取源码
官方渠道只有一个——kernel.org。别图省事去第三方站点下载,你没法保证源码的完整性。
下载时你会看到三种压缩格式:.tar.xz、.tar.gz、.tar.bz2。我个人推荐xz格式,压缩率最高,体积最小。解压命令很简单:
# 下载主线稳定版
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.8.tar.xz
# 下载对应的签名文件
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.8.tar.sign
# 解压
tar -xf linux-6.6.8.tar.xz
cd linux-6.6.8/
解压完你会看到大约3万多个文件。别慌,大部分是驱动和架构相关代码。我们裁剪优化的目标,就是从中挑出你硬件真正需要的那部分。
内核配置系统:Kconfig与Makefile
内核的配置系统其实就两样东西:Kconfig定义「有什么选项」,Makefile定义「怎么编译这些选项」。你想想看,整个内核几千个驱动,总不能每次都手动改Makefile吧?Kconfig就是来解决这个问题的。
Kconfig语法速览
随便打开一个驱动目录下的Kconfig文件,你会看到类似这样的结构:
config SENSORS_LM90
tristate "LM90 and compatible temperature sensors"
depends on I2C
help
If you say yes here, you get support for ...
This driver can also be built as a module.
这里的关键字我解释一下:
- tristate:三种状态——编译进内核(y)、编译为模块(m)、不编译(n)。驱动类选项基本都是tristate。
- bool:只有y/n两种状态,通常用于核心功能开关。
- depends on:依赖关系。比如这个温度传感器依赖I2C子系统,如果I2C没选,它就不会出现。
- select:反向依赖。选了A,自动把B也选上。这个要小心用,容易造成循环依赖。
避坑指南:我曾经在一个项目里,为了省空间把某个驱动设为m(模块),结果忘了把它的依赖也设为模块。启动时内核报错说符号找不到,排查了整整半天。后来我养成了习惯——每次改完配置都跑一遍make olddefconfig,它能自动处理依赖关系。
Makefile如何配合Kconfig
Kconfig定义完选项,Makefile负责执行。看个例子:
# drivers/hwmon/Makefile
obj-$(CONFIG_SENSORS_LM90) += lm90.o
这里的CONFIG_SENSORS_LM90就是Kconfig里定义的选项名。当你通过menuconfig把它设为y时,这个变量就变成y,等价于obj-y += lm90.o,lm90.c就会被编译进内核。如果设为m,就变成obj-m,编译成模块。
这套机制说白了就是「变量替换」。你通过配置界面设置变量值,Makefile根据变量值决定编译行为。理解了这个,你就知道裁剪内核其实就是「把不需要的选项对应的变量设为空」。
使用menuconfig进行基础配置
配置内核有很多种方式:menuconfig、nconfig、xconfig、gconfig。我几乎只用menuconfig,原因很简单——它在终端里跑,不需要图形界面,SSH连到开发板或服务器上也能用。
启动menuconfig
# 先确保安装了ncurses库
sudo apt install libncurses-dev
# 进入内核源码目录,启动配置界面
make menuconfig
你会看到一个蓝底灰字的菜单界面。操作很直观:
- 方向键:上下移动光标
- Enter:进入子菜单
- Y:选中(编译进内核)
- M:编译为模块
- N:不编译
- ?:查看帮助
- /:搜索选项
/键输入关键词,比如搜"USB",它会列出所有包含USB的选项,并告诉你每个选项在菜单树里的路径。我每次裁剪新板子,都是先搜硬件相关的关键词,然后逐个定位配置。
基础配置流程
拿到一个新板子,我一般按这个顺序配置:
- 设置架构:
General setup —> Cross-compiler tooltip,指定交叉编译工具链前缀。 - 选择CPU型号:
Processor type and features,选对CPU系列,比如ARM Cortex-A7就选ARMv7。 - 配置驱动:
Device Drivers,只选板子上实际用到的外设驱动。 - 关闭调试:
Kernel hacking,把调试选项全关掉,能省不少空间。 - 文件系统支持:
File systems,选你用的文件系统类型,比如ext4、squashfs。
这里我特别想强调一点:不要贪多。很多人觉得「多选几个驱动又不会死」,但每个选中的驱动都会增加内核体积和启动时间。我做过一个极端案例——把某款路由器内核从4.2MB裁到1.1MB,就是靠一个个关掉不需要的驱动。
保存与加载配置
配置完成后,按Exit退出,系统会问你是否保存。保存后生成.config文件。这个文件就是你的配置快照。
# 保存当前配置
cp .config config_my_board
# 下次加载
cp config_my_board .config
make olddefconfig # 更新依赖关系
make menuconfig # 微调
知识体系总览
下面这张图总结了内核源码获取与配置的核心流程,你可以对照着梳理自己的操作步骤:
几个实用技巧
最后分享几个我实际项目中积累的小技巧:
- 用
make savedefconfig生成最小配置:它会去掉所有默认值,只保留你修改过的选项。这个文件特别适合版本管理,只有几十行。 - 对比两个.config文件:用
diff命令对比不同版本的配置,能快速看出改了哪些选项。 - 善用
make localmodconfig:如果你已经在目标板子上跑了一个完整内核,这个命令会根据当前加载的模块自动生成最小配置。我第一次用的时候省了至少两小时的手动配置时间。