内核源码获取与配置:从kernel.org到menuconfig

做嵌入式Linux裁剪,第一步就是拿到内核源码。这事听起来简单,但里面坑不少。我见过不少新手直接从网盘下载所谓「优化版」内核,结果后面调试时连问题出在哪都找不到。今天咱们就聊聊怎么正规地获取源码,以及怎么用Kconfig/Makefile这套工具把配置玩明白。

从kernel.org获取源码

官方渠道只有一个——kernel.org。别图省事去第三方站点下载,你没法保证源码的完整性。

我的习惯:每次下载完都校验签名。Linus Torvalds和Greg Kroah-Hartman的PGP公钥很容易找到,花两分钟验一下,能避免后面几天的排查痛苦。

下载时你会看到三种压缩格式:.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万多个文件。别慌,大部分是驱动和架构相关代码。我们裁剪优化的目标,就是从中挑出你硬件真正需要的那部分。

注意:不要用root用户直接解压到系统目录。我习惯在/home下建一个workspace目录,专门放内核源码。权限问题少,编译时也方便。

内核配置系统: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的选项,并告诉你每个选项在菜单树里的路径。我每次裁剪新板子,都是先搜硬件相关的关键词,然后逐个定位配置。

基础配置流程

拿到一个新板子,我一般按这个顺序配置:

  1. 设置架构:General setup —> Cross-compiler tooltip,指定交叉编译工具链前缀。
  2. 选择CPU型号:Processor type and features,选对CPU系列,比如ARM Cortex-A7就选ARMv7。
  3. 配置驱动:Device Drivers,只选板子上实际用到的外设驱动。
  4. 关闭调试:Kernel hacking,把调试选项全关掉,能省不少空间。
  5. 文件系统支持:File systems,选你用的文件系统类型,比如ext4、squashfs。

这里我特别想强调一点:不要贪多。很多人觉得「多选几个驱动又不会死」,但每个选中的驱动都会增加内核体积和启动时间。我做过一个极端案例——把某款路由器内核从4.2MB裁到1.1MB,就是靠一个个关掉不需要的驱动。

保存与加载配置

配置完成后,按Exit退出,系统会问你是否保存。保存后生成.config文件。这个文件就是你的配置快照。

# 保存当前配置
cp .config config_my_board

# 下次加载
cp config_my_board .config
make olddefconfig   # 更新依赖关系
make menuconfig     # 微调
千万别直接编辑.config文件!我刚开始做嵌入式时,觉得手动改.config更快,结果改完依赖关系全乱套,编译报错几百个。老老实实用menuconfig,它帮你做依赖检查。

知识体系总览

下面这张图总结了内核源码获取与配置的核心流程,你可以对照着梳理自己的操作步骤:

内核源码获取与配置流程 内核源码获取与配置流程 1. 获取源码 kernel.org 下载 校验PGP签名 tar -xf 解压 2. 配置系统 Kconfig 定义选项 Makefile 执行编译 tristate / bool / depends 3. menuconfig 选择架构/CPU 配置驱动/文件系统 关闭调试选项 输出:.config 配置文件 核心原则 只选硬件需要的驱动 → 减少内核体积 → 缩短启动时间 → 降低内存占用

几个实用技巧

最后分享几个我实际项目中积累的小技巧:

  • 用make savedefconfig生成最小配置:它会去掉所有默认值,只保留你修改过的选项。这个文件特别适合版本管理,只有几十行。
  • 对比两个.config文件:用diff命令对比不同版本的配置,能快速看出改了哪些选项。
  • 善用make localmodconfig:如果你已经在目标板子上跑了一个完整内核,这个命令会根据当前加载的模块自动生成最小配置。我第一次用的时候省了至少两小时的手动配置时间。