第四章:内核配置与裁剪——给系统“瘦身”

内核配置,说白了就是给你的嵌入式系统选一套最合适的“器官”。驱动装多了,系统臃肿、启动慢;装少了,外设不工作。我刚开始做项目时,总想着“多配点总没错”,结果一个内核镜像快赶上树莓派那么大,烧进去才发现根本跑不动。

这一章,我们就来聊聊怎么用 menuconfig 把内核“瘦”下来,同时把调度器和 cgroup 调好,让 MQTT 通信跑得更稳。

4.1 进入 menuconfig:你的内核“控制面板”

在 Buildroot 里配置内核,其实就一条命令:

make linux-menuconfig

执行后你会看到一个蓝底黄字的界面。嗯,别被它复古的 UI 吓到,这玩意儿比很多图形工具都靠谱。我个人习惯先按 / 键搜索功能,比如搜 MQTT 相关的依赖,再决定要不要打开某个选项。

小技巧: 每次修改完配置,记得把 .config 文件备份一份。我曾经手滑把配置清空了,结果重配花了一整个下午。

4.2 裁剪无用驱动:少即是多

嵌入式设备的外设是固定的。你不需要为一块不存在的显卡配驱动,也不需要为永远不会插的 USB 设备预留模块。裁剪的核心思路就一句话:只保留硬件需要的,其余全关。

我一般按这个顺序来裁剪:

  1. 关闭所有不用的架构支持 —— 比如你用的是 ARM Cortex-A7,就把 x86、MIPS、RISC-V 全关了。
  2. 关闭不用的文件系统 —— 只用 ext4 或 squashfs,就把 NTFS、XFS、Btrfs 关掉。
  3. 关闭不用的网络协议 —— 只跑 TCP/IP 和 MQTT,IPX、DECnet 这些古董协议直接禁用。
  4. 关闭调试选项 —— Kernel debugging、Debug filesystem 这些在发布版里必须关。
关键指标: 一个典型的 IoT 网关内核,裁剪后镜像大小应该控制在 2MB 以内。我见过有人把内核压到 800KB,照样跑 MQTT broker 跑得飞起。

4.3 配置 cgroup:给 MQTT 进程“划地盘”

cgroup(控制组)是 Linux 内核用来限制、记录和隔离进程资源使用的机制。对于 MQTT 通信来说,cgroup 能保证即使系统其他部分负载飙升,MQTT broker 也不会被饿死。

在 menuconfig 里,你需要确保以下选项被打开:

配置项 路径 说明
CONFIG_CGROUPS General setup → Control Group support cgroup 总开关,必须开启
CONFIG_CGROUP_CPUACCT General setup → CPU accounting controller 统计 MQTT 进程的 CPU 使用量
CONFIG_CGROUP_MEM_RES_CTLR General setup → Memory resource controller 限制 MQTT 进程的内存上限
CONFIG_CGROUP_SCHED General setup → Group CPU scheduler 让调度器按 cgroup 分组分配 CPU 时间
注意: 开启 cgroup 会增加少量内核开销(约 10-20KB),但对于多进程的 MQTT 场景,这点代价完全值得。我曾经在一个 64MB 内存的设备上,靠 cgroup 把 MQTT broker 的内存占用稳定控制在 8MB 以内。

4.4 调度器选择:让 MQTT 线程“插队”

Linux 默认的调度器是 CFS(完全公平调度器),它对大多数场景都友好。但 MQTT 通信有个特点:消息收发是突发性的。平时负载很低,但一旦有大量消息涌入,CPU 需要立刻响应。

我个人建议在 menuconfig 里开启 CONFIG_SCHED_DEBUG 和 CONFIG_SCHEDSTATS,这样可以在运行时用 chrt 命令调整 MQTT broker 的调度策略:

# 将 MQTT broker 设为实时 FIFO 调度,优先级 50
chrt -f -p 50 $(pidof mosquitto)

为什么用 FIFO?因为 MQTT 的 publish 操作需要快速完成,不能被其他普通进程打断。当然,别把优先级设太高(比如 99),否则系统关键进程可能被饿死。

4.5 生成精简内核:一步到位

配置完成后,退出 menuconfig 并保存。回到 Buildroot 根目录,执行:

make linux-rebuild

Buildroot 会重新编译内核。编译完成后,你可以在 output/images/ 下找到新内核镜像。我习惯用 ls -lh 看一眼大小,如果比预期大很多,就回去再检查一遍配置。

验证方法: 把新内核烧到设备上,启动后用 cat /proc/config.gz | zcat | grep -i "mqtt\|cgroup" 确认关键选项已生效。

4.6 本章知识体系

下面这张图总结了内核配置与裁剪的核心流程,你可以把它当作操作清单:

内核配置与裁剪核心流程 进入 menuconfig 裁剪无用驱动 配置 cgroup 选择调度器 生成精简内核 关键检查点 • 镜像 < 2MB • cgroup 选项全开 • 调度器选 FIFO • 调试选项全关 • 文件系统精简 • 网络协议裁剪 • 架构支持最小化 • 备份 .config • 烧录后验证

这张图把整个流程串起来了。你从 menuconfig 开始,依次完成裁剪、cgroup 配置、调度器选择,最后生成镜像。每一步都有对应的检查点,照着做基本不会出错。

4.7 避坑指南

最后分享几个我踩过的坑:

  • 别把内核裁剪得太狠 —— 我试过关掉 CONFIG_EPOLL,结果 MQTT broker 直接无法启动,因为 libwebsockets 依赖 epoll。
  • cgroup 的 memory 限制别设太死 —— 给 MQTT broker 留 20% 的余量,否则突发流量时进程会被 OOM killer 干掉。
  • 调度器优先级别乱调 —— 我曾经把系统 watchdog 的优先级调低了,结果设备死机了都没人知道。

嗯,内核配置这事儿,说白了就是“够用就好”。多一分冗余是浪费,少一分功能是缺陷。等你多裁几次,自然就找到感觉了。