4、内核配置基础:make menuconfig、配置选项分类、保存与加载配置

好,咱们进入内核裁剪的第一道门槛——配置。说实话,很多新手一上来就急着改代码、加驱动,结果连内核怎么配的都没搞明白。我当年也犯过这毛病,编译出来的内核要么缺功能,要么大得像头牛。今天咱们就把这块地基打扎实。

4.1 make menuconfig:图形化配置工具

内核配置工具其实有好几种,比如 make config(命令行问答式)、make xconfig(Qt图形界面)、make gconfig(GTK图形界面)。但车载项目里,我几乎只用 make menuconfig。为什么?因为它基于 ncurses,终端里就能跑,不依赖桌面环境。你在服务器上 SSH 连过去,照样用得飞起。

用法很简单:

$ cd /path/to/linux-kernel
$ make menuconfig

进去之后你会看到一个蓝底灰字的界面。上下键移动光标,回车进入子菜单,空格或 Y 键选中,N 键取消,M 键编译成模块。嗯,这里有个小细节——Y 是编译进内核,M 是编译成 .ko 模块,N 就是不编译。车载系统里,我建议能编进内核的就别用模块,减少文件系统负担,也避免模块加载失败导致功能缺失。

我的习惯:每次改配置前,先执行 make mrproper 清理一下旧配置。别问我为什么,有一次我偷懒没清,结果新旧配置混在一起,编译出来的内核在板子上跑着跑着就挂了。排查了两天才发现是配置残留的问题。

4.2 配置选项分类:别被海量选项吓到

第一次打开 menuconfig,你可能会被密密麻麻的选项吓到。别慌,内核配置虽然多,但分类很清晰。咱们车载中控重点关注这几类:

分类 说明 车载重点关注
General setup 通用设置,如内核版本、Cgroups、命名空间 开启 Cgroups 用于容器化应用
Processor type and features CPU 架构、多核调度、电源管理 根据 SoC 选择,比如 ARM Cortex-A 系列
Power management and ACPI 电源管理、休眠、频率调节 车载必须开启,否则待机功耗扛不住
Device Drivers 设备驱动,这是最庞大的部分 只保留 LCD、触摸、音频、WiFi/BT、CAN 等
File systems 文件系统支持 ext4、squashfs(只读根文件系统)、overlayfs
Networking support 网络协议栈 TCP/IP、CAN 总线、蓝牙协议

你想想看,一个完整的内核源码有上万个配置项,但车载中控真正用到的可能不到 20%。剩下的全是冗余。我见过有人直接把 defconfig 拿来用,编译出来的内核 50MB,塞进 eMMC 里启动都要半分钟。裁剪之后,10MB 以内就能搞定。

核心原则:只配你需要的,关掉所有不确定的。宁可少一个功能,也别多一个隐患。车载系统对稳定性要求极高,多一个驱动就多一个崩溃点。

4.3 保存与加载配置:别让你的努力白费

配置完了,怎么保存?其实 menuconfig 退出时会自动提示你保存到 .config 文件。但这里有个坑——.config 文件是隐藏文件,而且不同内核版本之间不兼容。我曾经把 4.19 的 .config 直接拿到 5.4 上用,编译报了一堆错,最后发现是配置项名字变了。

正确的做法是:

# 保存当前配置
$ cp .config my_car_defconfig

# 加载配置
$ cp my_car_defconfig .config
$ make olddefconfig   # 自动处理新版本新增的配置项

make olddefconfig 这个命令很实用。它会读取你现有的 .config,然后对新版本内核里新增的配置项自动选择默认值。这样你就不用从头再配一遍了。

我曾经踩过的坑:有一次我直接 make savedefconfig 生成了一个精简版的 defconfig,结果发现它把很多依赖项给省略了。后来编译出来的内核缺少 DMA 支持,触摸屏怎么点都没反应。所以,保存完整 .config 比保存 defconfig 更安全,除非你非常清楚哪些是默认值。

4.4 配置依赖与反向依赖

内核配置不是孤立的,选项之间有依赖关系。比如你选了 CONFIG_TOUCHSCREEN,它可能依赖 CONFIG_INPUTCONFIG_I2C。如果你没选这些依赖项,menuconfig 会自动帮你选上,或者直接灰掉让你选不了。

但反向依赖就麻烦了。比如你选了 CONFIG_USB,它可能会被很多驱动依赖。你关掉 USB,结果发现蓝牙、WiFi、甚至某些存储设备都跟着没了。我建议用这个命令查看依赖关系:

$ make menuconfig
# 在界面中按 / 键搜索配置项,比如输入 TOUCHSCREEN
# 会显示它的依赖、选择、以及被哪些选项依赖

车载系统里,我习惯先画一个功能框图,把需要的硬件列出来,然后反推需要哪些内核配置。比如:

  • LCD 显示 → DRM/KMS 框架、GPU 驱动、背光驱动
  • 触摸屏 → I2C 控制器、输入子系统、触摸屏驱动
  • 音频 → ALSA 框架、Codec 驱动、I2S 控制器
  • 车载网络 → CAN 总线驱动、SocketCAN 协议

这样一层层推下去,你就知道该开哪些选项了。

4.5 实战建议:从零开始还是基于现有配置?

这个问题我经常被问到。我的建议是:别从零开始。内核源码里每个架构都带了一个默认配置,比如 arch/arm/configs/ 下面有很多 xxx_defconfig。你可以找一个最接近你硬件的,然后在此基础上裁剪。

# 以 ARM 架构为例
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
    my_car_defconfig   # 假设你保存了自定义配置

# 或者直接用厂商提供的 defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
    imx_v7_defconfig

拿到基础配置后,我会先做一次全编译,确保能跑起来。然后逐步关掉不需要的选项,每关一批就编译一次,验证功能是否正常。这样万一出了问题,你能很快定位到是哪个配置引起的。

小技巧:make diffconfig 可以比较两个 .config 文件的差异。我在做版本升级时,经常用这个命令看看新版本改了哪些配置,心里有个底。

好了,这一章的内容就到这儿。配置内核就像搭积木,一开始觉得眼花缭乱,但摸清规律后,你会发现其实就那么回事。下一章咱们聊聊具体的驱动裁剪,我会拿一个真实的车载项目来演示,怎么把 50MB 的内核砍到 8MB。