4、内核配置系统详解:从界面到底层逻辑

内核配置,说白了就是告诉 Linux 内核:「你要支持哪些硬件?要开启哪些功能?」。这一步没做好,后面编译出来的内核要么跑不起来,要么臃肿得像头大象。我刚开始做嵌入式时,就吃过这个亏——编译了个全功能内核,结果 8MB 的 Flash 根本放不下。

这一章,我们就把内核配置系统彻底讲透。从交互界面到配置文件结构,再到裁剪原则,一条线串下来。

4.1 四种配置界面:你该用哪个?

Linux 内核提供了四种配置工具,各有各的适用场景。我列个表,你一看就明白。

命令 界面类型 适用场景 依赖
make config 命令行问答 脚本自动化、极简环境 无
make menuconfig 文本菜单 (ncurses) 嵌入式开发最常用 libncurses-dev
make xconfig 图形界面 (Qt) 桌面开发、新手友好 qt5-dev-tools
make gconfig 图形界面 (GTK+) GNOME 环境用户 libgtk-3-dev

我个人习惯用 make menuconfig。为什么?因为它不依赖图形桌面,在 SSH 终端里就能跑,而且操作效率极高。你想想看,嵌入式开发经常要连开发板、连服务器,哪有那么多图形界面给你用?

小技巧: 在 menuconfig 里按 / 键可以搜索配置项。比如你想找 USB 相关的选项,直接按 / 输入 USB,所有匹配项就列出来了。这个功能我几乎每天都在用。

4.2 配置选项的依赖关系:别乱勾

内核配置项不是独立的。它们之间有复杂的依赖关系。你勾了一个选项,可能自动带出来一堆依赖项;你取消一个选项,也可能导致其他选项变灰。

这种依赖关系在 Kconfig 文件中定义。每个配置项都有三种基本属性:

  • bool:要么编译进内核 (Y),要么不编译 (N)。
  • tristate:三种状态——编译进内核 (Y)、编译成模块 (M)、不编译 (N)。
  • depends on:依赖条件。只有依赖项满足时,当前选项才可见。
  • select:反向依赖。选中当前项时,自动选中依赖项。

举个例子,你打开 CONFIG_USB_SUPPORT 后,CONFIG_USB_EHCI_HCD 才会出现。这就是 depends on 在起作用。

我曾经踩过的坑: 有一次我为了省空间,把 CONFIG_FW_LOADER 取消了。结果网卡驱动死活加载不了固件,系统启动时一直报 firmware 加载失败。查了半天才发现,网卡驱动依赖这个选项。所以,不要随意取消你不认识的选项,尤其是那些被 select 反向依赖的。

4.3 .config 文件结构:内核配置的「源代码」

所有配置最终都保存在内核源码根目录下的 .config 文件中。这个文件是纯文本格式,每一行就是一个配置项。

# 注释行,说明配置项的作用
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSVIPC=y

# 模块配置,=m 表示编译成模块
CONFIG_USB=m
CONFIG_USB_EHCI_HCD=m

# 未配置的选项,前面加 # 注释
# CONFIG_USB_OTG is not set

# 字符串和整数配置
CONFIG_DEFAULT_HOSTNAME="(none)"
CONFIG_LOG_BUF_SHIFT=17

结构其实很简单:

  • =y:编译进内核
  • =m:编译成模块
  • # ... is not set:不编译
  • ="value":字符串或数值参数

你完全可以手动编辑这个文件。但我建议你只在特殊情况下这么做——比如批量修改几个选项时。平时还是用 menuconfig,因为它会自动处理依赖关系。

核心原则: 每次修改 .config 后,一定要执行 make olddefconfig 来同步依赖关系。这个命令会自动补全缺失的选项,并设置合理的默认值。我每次手动改完 .config,第一件事就是跑这个命令。

4.4 内核配置裁剪原则:少即是多

嵌入式设备资源有限。Flash 可能只有 4MB、8MB,RAM 可能只有 64MB。内核必须「量体裁衣」。下面这张图展示了裁剪的核心逻辑:

内核配置裁剪核心流程 1. 硬件平台分析 2. 最小化核心功能 3. 模块化驱动 4. 移除调试选项 关闭不用的文件系统 关闭不用的网络协议 (如 IPv6、AX.25 等) 驱动尽量编译为模块 运行时按需加载 (减少内核镜像体积) 关闭 DEBUG 选项 关闭 PRINTK 时间戳 (节省内存和 CPU) 5. 编译验证 & 功能测试

裁剪不是盲目删东西。我总结了一个「三步走」原则:

  1. 先跑起来,再瘦身:第一次配置时,先用默认配置编译一个能启动的内核。确保硬件能跑通,再开始裁剪。
  2. 按需裁剪,逐项确认:每关掉一个功能,都要问自己:「这个功能我的设备真的不需要吗?」。比如你的设备只用以太网,那把 WiFi、蓝牙、CAN 总线都关掉。
  3. 模块化,不固化:驱动尽量编译成模块 (=m)。这样内核镜像小,运行时按需加载。我见过有人把所有驱动都编进内核,结果镜像 5MB,Flash 都放不下。
一个实用的裁剪清单:
  • 关闭 CONFIG_DEBUG_KERNEL 及其子选项(生产环境不需要内核调试)
  • 关闭 CONFIG_PRINTK 或降低日志缓冲区大小(节省内存)
  • 关闭不用的文件系统(如 XFS、Btrfs、NTFS)
  • 关闭不用的网络协议(如 IPv6、DECnet、IPX)
  • 关闭不用的驱动(如显卡、声卡、TV 调谐器)
我的经验: 裁剪完成后,用 make savedefconfig 生成一个最小化的 defconfig 文件。这个文件只包含你修改过的选项,干净利落。下次编译时,直接 make defconfig 就能恢复你的配置。我每个项目都会保留一份这样的 defconfig,方便复用。

嗯,内核配置系统就讲到这里。记住:配置不是一次性的工作。随着硬件迭代、需求变化,你随时可能回来调整。掌握这些工具和原则,你就能游刃有余地应对各种场景。