第四章:自定义Linux内核——内核配置裁剪、移除不必要的驱动、启用安全子系统

说实话,内核定制这件事,很多嵌入式工程师一开始都觉得「没必要」。默认内核能跑就行,干嘛要折腾?我在早期项目中也这么想,直到有一次产品送审,安全扫描报告直接打回来——未使用的驱动模块存在漏洞风险。嗯,从那以后,内核裁剪就成了我每个项目的必修课。

这一章,我们就来聊聊怎么把内核「瘦身」并「上锁」。说白了,就是两件事:去掉你用不到的,加上你必须有的。

4.1 内核配置裁剪:从「大而全」到「小而精」

Yocto 构建内核时,默认配置往往包含了大量你根本用不到的驱动和功能。比如一个简单的 IoT 传感器节点,内核里却编译了显卡驱动、声卡驱动、各种文件系统支持……这不仅是浪费,更是安全隐患。

我个人习惯的做法是:先跑一遍默认配置,然后逐项裁剪。具体操作分三步:

  1. 生成默认配置:在 Yocto 的 Linux 内核源码目录下,执行 bitbake -c menuconfig virtual/kernel,打开图形化配置界面。
  2. 逐项检查:对照你的硬件清单,把不需要的驱动、文件系统、网络协议全部去掉。
  3. 保存配置:将裁剪后的配置保存为 defconfig,并放入你的 Yocto 层中。

核心原则:只保留你硬件实际使用的驱动和功能。一个驱动都不要多。

举个例子,我在做一个基于 ARM Cortex-M4 的工业控制器时,发现默认内核里居然包含了 CONFIG_SOUNDCONFIG_VIDEO 相关模块。我们的产品根本没有音频和视频接口,这些驱动完全是多余的。去掉之后,内核镜像从 4.2MB 降到了 1.8MB,启动时间也快了将近 1 秒。

4.2 移除不必要的驱动:避坑指南

移除驱动听起来简单,但实际操作中容易踩坑。我曾经因为删掉了一个看似无关的 CONFIG_GPIO_SYSFS,导致整个 GPIO 子系统无法通过用户空间访问,调试了两天才发现原因。

这里分享几个我总结的要点:

  • 先备份,再裁剪:每次修改配置前,把原始 .config 文件备份一份。别问我为什么强调这个,问就是吃过亏。
  • 使用 make savedefconfig:这个命令会生成一个精简版的配置,只包含非默认值的选项。方便你对比和审查。
  • 关注依赖关系:Linux 内核的 Kconfig 系统有复杂的依赖链。你删掉一个选项,可能连带删掉其他功能。建议用 make xconfig 查看依赖关系图。

注意:不要盲目删除 CONFIG_* 选项。特别是 CONFIG_OF(设备树支持)、CONFIG_IRQ(中断控制器)这类基础选项,删了系统直接起不来。

我建议的做法是:先列出你的硬件清单,包括 CPU、外设、总线接口等。然后对照内核配置,逐项确认。比如你的板子上有 I2C 设备,就保留 CONFIG_I2C 和对应的驱动;没有 SPI,就果断去掉 CONFIG_SPI

4.3 启用安全子系统:SELinux vs AppArmor

安全子系统是内核加固的重头戏。目前主流的选择有两个:SELinux 和 AppArmor。你可能会问,到底选哪个?

我个人经验是:如果你的产品需要细粒度的强制访问控制(MAC),并且团队有足够的安全知识储备,选 SELinux。如果你更看重易用性和快速部署,AppArmor 更友好。

特性 SELinux AppArmor
安全模型 基于标签(Label) 基于路径(Path)
配置复杂度
策略语言 复杂,学习曲线陡 简单,类似白名单
适用场景 高安全要求(如军用、金融) 通用嵌入式设备

在 Yocto 中启用 SELinux,需要在 local.conf 中添加:

DISTRO_FEATURES_append = " selinux"
PREFERRED_PROVIDER_virtual/refpolicy ?= "refpolicy-minimum"

启用 AppArmor 则更简单:

DISTRO_FEATURES_append = " apparmor"

小技巧:初次启用安全子系统时,建议先设置为「许可模式」(permissive),只记录违规行为,不强制拦截。等策略完善后再切换到「强制模式」(enforcing)。

我记得有一个项目,客户要求通过 Common Criteria EAL4+ 认证。我们选择了 SELinux,光是策略文件就写了 3000 多行。但最终效果确实好,任何未经授权的进程访问都被严格阻断。说白了,安全没有捷径,投入多少就回报多少。

4.4 内核配置的 Yocto 集成

在 Yocto 中,内核配置的定制通常通过 .bbappend 文件实现。你可以在自己的 meta 层中创建一个 recipes-kernel/linux/linux-yocto_%.bbappend 文件,内容如下:

FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
SRC_URI_append = " file://my_defconfig"

do_configure_prepend() {
    cp ${WORKDIR}/my_defconfig ${S}/.config
}

这样,每次构建内核时,Yocto 会自动使用你定制的 my_defconfig 文件。我个人习惯把不同硬件平台的配置分开存放,比如 board_a_defconfigboard_b_defconfig,然后在 .bbappend 中根据 MACHINE 变量动态选择。

4.5 验证与测试

裁剪和加固完成后,一定要做两件事:

  • 功能测试:确保所有硬件外设正常工作。特别是网络、存储、通信接口。
  • 安全测试:使用工具如 checkseclynis 检查内核安全配置是否生效。

我曾经遇到一个案例,裁剪后系统启动正常,但运行 24 小时后突然死机。最后定位发现是去掉了 CONFIG_HIGH_RES_TIMERS,导致某个驱动的时间精度不够。所以,稳定性测试一定要跑够时间,别偷懒。

好了,这一章的内容就到这里。内核裁剪和安全加固是嵌入式安全的基础,也是最能体现工程师功底的地方。下一章我们会聊聊文件系统安全,包括只读挂载、完整性校验等话题。到时候见。