3、内核配置与裁剪:从“臃肿”到“精干”的必修课

说实话,我刚入行做车载Linux那会儿,最头疼的就是内核配置。动不动就几百兆的vmlinux,烧进去连启动都费劲。后来我明白了——内核裁剪不是炫技,是生存技能。车载系统资源就那么点,你多开一个模块,可能就多一次崩溃的风险。

这一章,咱们就聊聊怎么把内核从“大胖子”变成“精干小伙”。

3.1 内核配置选项详解:别被Kconfig吓到

内核的配置系统,说白了就是一套菜单系统。每个驱动、每个功能模块,都对应一个Kconfig文件。你运行make menuconfig,看到的就是这些Kconfig渲染出来的界面。

我习惯把配置选项分成三类:

类型 符号 含义 典型场景
内置 y 编译进内核镜像,常驻内存 必须用的驱动(如串口、中断控制器)
模块 m 编译成.ko文件,按需加载 不常用但偶尔需要的驱动(如USB摄像头)
禁用 n 完全不编译 硬件上没有的功能(如Wi-Fi模块)

这里有个坑——别一股脑全选y。我见过有人把内核编到50MB,结果启动时内存直接爆了。车载系统里,y只给那些启动阶段就必须用的东西。其他的,能m就m,能n就n。

我的小技巧: 每次改配置前,先跑一遍make savedefconfig,生成一个最小化的defconfig。这样你改了什么,一目了然。

3.2 裁剪原则:少即是多,但不是乱删

裁剪不是让你瞎删。你得有原则。我总结了四条铁律:

  1. 硬件决定论:板子上没有的硬件,对应的驱动直接n。比如你的SoC没有GPU,那DRM、GPU驱动统统关掉。
  2. 启动路径优先:从uboot到根文件系统挂载,这条路径上的驱动必须y。其他的,等系统起来了再加载也不迟。
  3. 功能最小化:车载系统不需要桌面、不需要蓝牙耳机、不需要打印机。这些子系统,能关就关。
  4. 调试符号剥离:发布版本别带DEBUG选项,那玩意儿能让内核体积翻倍。

我曾经在一个项目里,把内核从12MB裁到3.2MB。怎么做到的?就是把所有没用过的驱动全关了。你想想看,一个车载信息娱乐系统,要什么CONFIG_NFTABLES?要什么CONFIG_WIRELESS?统统干掉。

警告: 别为了追求极致体积,把CONFIG_EXT4_FS也关了。我见过有人裁完内核,结果根文件系统挂不上——因为文件系统驱动没了。嗯,那场面,相当尴尬。

3.3 最小内核配置实战:手把手教你“瘦身”

好,理论说完了,咱们来点实际的。假设你手里有一块i.MX6ULL的板子,256MB RAM,256MB NAND Flash。目标是跑一个车载仪表盘系统。

3.3.1 第一步:生成基准配置

# 先用默认配置
make imx_v6_v7_defconfig

# 看看有多大
make -j4
ls -lh arch/arm/boot/zImage
# 输出:-rw-r--r-- 1 root root 5.2M ... zImage

5.2MB,还行。但咱们的目标是2MB以内。继续。

3.3.2 第二步:关闭不需要的子系统

进入make menuconfig,按下面这个清单操作:

路径 选项 操作 节省空间
General setup CONFIG_SWAP 关(n) ~50KB
Networking support CONFIG_NET 如果不需要网络,直接关 ~500KB
Device Drivers → Graphics support CONFIG_DRM 保留fbdev,关掉DRM ~300KB
File systems CONFIG_EXT2_FS, CONFIG_BTRFS_FS等 只保留ext4和squashfs ~200KB
Kernel hacking CONFIG_DEBUG_KERNEL 全部关掉 ~1MB

这一步做完,再编译看看:

make -j4
ls -lh arch/arm/boot/zImage
# 输出:-rw-r--r-- 1 root root 2.8M ... zImage

2.8MB,离目标还差一点。

3.3.3 第三步:驱动级裁剪

这是最费功夫的一步。你得知道板子上到底有哪些硬件。我一般这么干:

  1. 看原理图,列出所有外设的型号。
  2. 在menuconfig里搜索对应的驱动,只保留这些。
  3. 其他的,比如CONFIG_TOUCHSCREEN、CONFIG_SOUND,如果不用,直接关。

举个例子,i.MX6ULL的LCD控制器是mxsfb,那就只保留这个:

Device Drivers → Graphics support → 
  [*] Support for frame buffer devices → 
    [*] MXS LCDIF framebuffer support
  # 其他的fb驱动全关

再编译:

make -j4
ls -lh arch/arm/boot/zImage
# 输出:-rw-r--r-- 1 root root 1.9M ... zImage

1.9MB,搞定。

关键指标: 裁剪后的内核,启动时间从原来的8秒降到了3.2秒。内存占用从64MB降到了28MB。这就是裁剪的价值。

3.4 知识结构图:内核裁剪的核心逻辑

下面这张图,是我自己总结的裁剪决策流程。你照着走,基本不会出错。

内核裁剪决策流程图 内核裁剪决策流程 获取硬件原理图 该硬件是否必须? 是 保留为 y 或 m 否 直接关闭 (n) 编译 & 启动验证 裁剪完成 提示:每次只改一类配置 方便回退排查

你看,整个流程其实不复杂。核心就一句话:用不到的,就别编进去。

3.5 避坑指南:我踩过的那些雷

最后,分享几个我亲身经历过的坑:

  • 别信“默认配置”:厂商给的defconfig,往往开了很多你用不到的东西。我遇到过一块板子,默认配置里居然有CONFIG_WIFI_BCM4334,但板子上根本没有Wi-Fi芯片。
  • 小心“依赖链”:你关了一个选项,可能连带关掉其他依赖项。比如关了CONFIG_INPUT,所有输入设备驱动都没了。嗯,我当时连按键都用不了。
  • 启动日志是好朋友:裁剪后第一次启动,一定要开earlyprintk。如果内核panic了,你能看到最后一条日志,就知道是哪个驱动出了问题。
  • 保留一个“安全内核”:我习惯在SD卡里存一份没裁剪的内核。万一裁坏了,还能从SD卡启动救回来。
我的习惯: 每次裁剪完,我都会跑一遍make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules -j4,然后对比前后体积。如果体积没怎么变,说明你还没裁到点子上。