4. 内核裁剪基础:make menuconfig 使用、模块与内建的选择策略、裁剪掉不需要的驱动

内核裁剪,说白了就是给你的 Linux 系统「瘦身」。嵌入式设备资源有限,Flash 和 RAM 都金贵得很。你想想看,一个完整的内核动辄几十兆,塞进只有 8MB Flash 的板子里,根本不可能。所以,我们得学会怎么把不需要的东西统统砍掉。

我个人习惯,拿到一块新板子,第一件事不是急着写驱动,而是先跑一遍 make menuconfig,把能关的先关了。这就像装修前先砸墙,把非承重墙拆了,空间才够用。

4.1 make menuconfig 的界面与操作

在源码根目录下执行 make menuconfig,你会看到一个基于 ncurses 的图形界面。别被它复古的外表吓到,这玩意儿效率极高。

界面主要分三块:

  • 顶部:操作提示栏,告诉你按什么键。
  • 中间:配置树,所有内核选项都挂在这里。
  • 底部:功能按钮,比如 Save、Load、Exit。

常用的快捷键就这几个:

按键 功能
Y 将选项编译进内核(内建)
M 将选项编译为模块
N 不编译该选项
Space 循环切换 Y / M / N
/ 搜索选项(支持模糊匹配)
? 查看选项的帮助信息
我的小技巧: 遇到不认识的选项,先按 ? 看帮助。很多选项的 help 里会写明它依赖哪些硬件,或者建议你选 Y 还是 M。这比瞎猜靠谱多了。

4.2 模块与内建的选择策略

这是内核裁剪的核心问题:某个功能,到底该编进内核(Y),还是编成模块(M),或者直接砍掉(N)?

我总结了一个「三问决策法」:

  1. 这个功能,系统启动时必须要有吗? 比如根文件系统所在的磁盘驱动、串口驱动。这些必须编进内核,否则系统都起不来。
  2. 这个功能,我需要在运行时动态加载/卸载吗? 比如 USB 摄像头驱动、Wi-Fi 驱动。这些做成模块,插上设备时自动加载,拔掉时自动卸载,省内存。
  3. 这个功能,我这辈子都用不上吗? 比如你用的是 ARM 板,那 x86 相关的驱动统统选 N。别犹豫,砍掉。

下面这张图,是我自己画的一个决策流程,帮你快速判断:

模块与内建选择决策流程 开始评估一个功能 Q1: 启动时必须要有? 是 否 选 Y:编译进内核 Q2: 需要动态加载? 是 否 选 M:编译为模块 选 N:直接砍掉 决策完成 说明:启动必须 = 根文件系统、串口、中断控制器等;动态加载 = USB、Wi-Fi、声卡等热插拔设备

核心原则:

  • 内建 (Y):启动依赖、性能敏感、不会变动的功能。
  • 模块 (M):热插拔设备、可选功能、调试工具。
  • 不编译 (N):与硬件无关、用不到、过时的功能。

4.3 裁剪掉不需要的驱动

这是最花时间,但也最见功夫的环节。我刚开始做裁剪时,总怕砍错了导致系统起不来。后来我学乖了——先做减法,再做加法。

具体步骤是这样的:

  1. 确定目标硬件:列出板子上所有的外设芯片型号。比如网卡是 DM9000,音频是 WM8960,等等。
  2. 保留必须的:CPU 架构、内存管理、进程调度、文件系统、串口。这些是底线,不能动。
  3. 批量关闭:在 Device Drivers 菜单下,把明显不相关的硬件驱动全部选 N。比如你的板子没有 PCIe 总线,那整个 PCIe 子系统都可以关掉。
  4. 按需开启:只把步骤 1 里列出的芯片驱动选上。能选模块的就选 M,不确定的先选 M。
  5. 编译测试:编译出内核和模块,烧到板子上跑一遍。如果某个功能不正常,再回来调整。
我曾经踩过的坑: 有一次我裁剪完内核,烧进去发现网卡不工作。查了半天,发现是 PHY 驱动没选。很多网卡驱动依赖底层的 PHY 驱动,但 menuconfig 里它们不在同一个菜单下。所以,一定要看驱动的依赖关系。按 / 搜索选项时,注意看 Depends on 那一行。

下面这张表,是我整理的一些常见可裁剪模块,供你参考:

子系统 常见可裁剪项 裁剪理由
网络 IPV6、WEXT、无线网卡驱动 嵌入式设备通常只用 IPv4,WEXT 已被 cfg80211 取代
音频 OSS 驱动、非板载声卡驱动 现在都用 ALSA,OSS 是过时的 API
USB USB 打印机、USB 存储、USB 串口 除非你的设备需要这些功能,否则全关
文件系统 NTFS、XFS、Btrfs、FAT(如果不用) 嵌入式常用 ext4、squashfs、jffs2,其他可以砍
图形 DRM 驱动(非本板卡)、FBDEV 驱动 只保留板子对应的显示驱动
虚拟化 KVM、Xen、VirtualBox 驱动 嵌入式设备一般不跑虚拟机

4.4 实战:从零开始裁剪一个最小内核

光说不练假把式。我们拿一个常见的 ARM 开发板(比如 S3C2440)来走一遍流程。

执行 make ARCH=arm menuconfig,然后:

  1. 关闭架构无关的选项:
    进入 General setup,关掉 Kernel .config support、Kernel log buffer size 可以调小(比如 64KB)。
  2. 关闭不用的总线:
    进入 Bus support,关掉 PCI、ISA、EISA。ARM 板一般只用 AMBA/AXI。
  3. 裁剪设备驱动:
    进入 Device Drivers,把 Network device support 里除了 DM9000 以外的网卡驱动全关。
    把 Input device support 里除了键盘、触摸屏以外的全关。
    把 Graphics support 里除了 LCD 控制器以外的全关。
  4. 精简文件系统:
    进入 File systems,只保留 ext4、squashfs、proc、sysfs、tmpfs。其他全关。
  5. 保存退出:
    按 Save,命名为 .config,然后退出。

这样一轮操作下来,一个原本 3MB 的内核,能压缩到 1.2MB 左右。如果再用 LZMA 压缩,最终镜像可能只有 600KB。嗯,这才像嵌入式该有的样子。

我的习惯: 每次裁剪完,我都会把 .config 文件备份一份,命名为 config_<日期>_<备注>。这样万一改坏了,还能快速回退。另外,make savedefconfig 可以生成一个最小化的配置文件,方便版本管理。

内核裁剪不是一锤子买卖。随着项目推进,你可能需要添加新的驱动,或者发现某个功能其实用不到。所以,保持 .config 的整洁,定期 review,是个好习惯。