第3章:Linux内核与根文件系统——内核配置裁剪与Buildroot构建

工控网关的软件栈,说白了就三层:引导程序、内核、根文件系统。内核是大脑,根文件系统是血肉。这一章,我带你亲手把这两样东西搞定。

你可能会问:直接用发行版的内核不行吗?行,但工控场景下,你不需要桌面、不需要显卡驱动、不需要一堆没用的模块。裁剪内核,一是为了减小体积,二是为了降低实时延迟——内核越小,调度路径越短。

本章核心目标:

  • 理解内核配置裁剪的基本方法
  • 打上PREEMPT_RT实时补丁,让Linux具备硬实时能力
  • 用Buildroot从零构建一个最小根文件系统

3.1 内核源码与实时补丁的获取

先讲个我踩过的坑。几年前做一款运动控制网关,内核用的4.19,没打实时补丁。现场跑起来,偶尔出现几毫秒的抖动,电机就跟着抖一下。客户差点退货。后来老老实实上了PREEMPT_RT,问题解决。

所以,工控网关选内核,我建议直接上LTS版本 + 对应的RT补丁。版本号必须严格对应,差一个次版本号都打不上。

组件 推荐版本 下载地址
Linux内核 5.10.x LTS kernel.org
PREEMPT_RT补丁 patch-5.10.x-rtxx rt.wiki.kernel.org
Buildroot 2023.02.x LTS buildroot.org

我的习惯:下载后先校验sha256sum,避免下载损坏。曾经有一次补丁文件不完整,编译到一半报错,排查了整整半天。

3.2 内核配置裁剪——做减法

内核配置,就是做减法。默认配置里有一大堆你永远用不到的东西:比如显卡驱动、无线网卡、蓝牙协议栈。工控网关只需要串口、网口、GPIO、I2C、SPI、CAN这些外设。

我一般这样操作:

  1. 先确定硬件平台——比如ARM Cortex-A7,用厂家提供的defconfig作为起点
  2. 运行make menuconfig——图形界面,逐项检查
  3. 关闭不需要的子系统——比如Sound card support、Multimedia support、GPU drivers
  4. 开启实时相关选项——这是关键

具体到实时配置,你需要打开以下选项:

General setup --->
  Preemption Model (Fully Preemptible Kernel (Real-Time)) --->
    [*] Preemptible RCU
    [*] High Resolution Timer Support
    [*] Enable RT mutexes

Kernel Features --->
  [*] Tickless System (Dynamic Ticks)
  [*] Full dynticks system (all CPUs)
  Timer frequency ---> 1000 Hz

Processor type and features --->
  [*] Support for real-time tasks
  [*] Force threading of IRQs

注意:开启Fully Preemptible Kernel后,内核吞吐量会下降约5%-10%。但工控场景下,确定性比吞吐量重要得多。我曾经在一条产线上测试过,抖动从原来的2ms降到了50μs以内。

裁剪完成后,保存为.config文件。我习惯把它备份一份,命名为config_rt_gateway,方便后续复用。

3.3 编译内核与设备树

配置好了,就该编译了。命令不多,但参数要写对:

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
make zImage -j4
make dtbs
make modules

编译完成后,你会得到三个关键产物:

  • zImage——压缩后的内核镜像,烧写到内核分区
  • *.dtb——设备树二进制文件,描述硬件资源
  • modules目录——内核模块,按需安装到根文件系统

这里有个细节:设备树一定要和硬件匹配。我见过有人拿错了dtb,结果网口不工作,排查了两天才发现是引脚复用配置错了。

3.4 Buildroot构建最小根文件系统

根文件系统,就是Linux启动后挂载的“/”目录。工控网关不需要桌面环境,不需要X11,甚至不需要bash——一个busybox就够了。

Buildroot是我最常用的工具。它不像Yocto那么重,也不像Debootstrap那么散。配置一次,编译一次,出来就是一个完整的根文件系统镜像。

我的配置思路:

组件类别 选择 说明
Target options ARM (little endian) 匹配你的CPU架构
Toolchain Buildroot internal 自动编译交叉工具链
System configuration Busybox + glibc 轻量级用户空间
Filesystem images ext4 / squashfs 生产环境推荐squashfs(只读)
Networking dropbear + libcurl SSH和HTTP客户端
OPC UA open62541 开源OPC UA实现

运行make menuconfig后,逐项勾选。我个人习惯先选一个最小配置,编译通过后再逐步添加功能。这样万一出问题,定位范围小。

避坑指南:我曾经一次性勾选了所有需要的包,结果编译到一半报依赖错误。后来学乖了,分三步走:先出最小系统,再加网络服务,最后加OPC UA库。每一步都编译验证一次。

3.5 根文件系统的目录结构

Buildroot编译完成后,在output/images目录下会生成rootfs.ext4或rootfs.squashfs。挂载后,你会看到这样的结构:

/
├── bin/          # busybox和基本命令
├── sbin/         # 系统管理命令
├── usr/          # 用户程序
├── etc/          # 配置文件
├── lib/          # 动态库
├── dev/          # 设备节点
├── proc/         # 进程文件系统
├── sys/          # 系统文件系统
├── tmp/          # 临时文件
├── var/          # 可变数据
└── mnt/          # 挂载点

这个结构比标准Linux发行版小得多——整个根文件系统只有8MB左右。你想想看,一个Ubuntu要几个GB,而工控网关只需要几MB。这就是裁剪的价值。

3.6 整合内核与根文件系统

内核和根文件系统都准备好了,接下来就是整合。我通常用SD卡或NAND Flash作为存储介质,分区方案如下:

分区 大小 内容
boot 16MB zImage + dtb + u-boot
rootfs 64MB 根文件系统(squashfs只读)
data 剩余空间 应用数据、日志、配置(可读写)

启动参数中,内核需要知道根文件系统在哪里:

root=/dev/mmcblk0p2 rootfstype=squashfs rw console=ttyS0,115200

这里有个小技巧:根文件系统用squashfs只读挂载,可以防止意外写坏。应用数据放在data分区,即使系统崩溃,配置和日志还在。

3.7 本章知识结构图

下面这张图,把内核裁剪、实时补丁、Buildroot构建的流程串起来了。你可以把它当作操作路线图:

Linux内核与根文件系统构建流程 内核裁剪 + 实时补丁 + Buildroot 构建流程 Linux内核源码 (5.10.x) PREEMPT_RT补丁 Buildroot配置 打补丁 + 配置裁剪 make menuconfig make (Buildroot) zImage + dtb 内核模块 rootfs.squashfs 烧写到目标板 (SD/NAND) 工控网关启动成功 ① 打补丁前确认版本号严格匹配 ② 裁剪时保留目标硬件驱动 ③ Buildroot分步编译,避免依赖错误

3.8 验证与测试

烧写完成后,上电启动。通过串口观察启动日志:

U-Boot 2023.04
...
Starting kernel ...
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.10.100-rt60
[    0.000000] Preemptible RCU enabled
[    0.000000] High-resolution timers enabled
...
[    2.345678] VFS: Mounted root (squashfs filesystem) readonly on device 179:2
[    2.456789] Freeing unused kernel memory: 1024K
[    2.567890] Run /sbin/init as init process
...
Welcome to Buildroot
gateway login: root

看到Preemptible RCU enabled和High-resolution timers enabled,说明实时补丁生效了。看到Mounted root (squashfs filesystem) readonly,说明根文件系统挂载成功。

登录后,运行uname -a确认内核版本,运行cat /proc/version查看实时配置。我一般还会跑一个简单的延迟测试:

cyclictest -t1 -p99 -n -i1000 -l100000

如果最大延迟在100μs以内,说明实时性达标。我在项目中测过,5.10内核打上RT补丁后,典型延迟在30-50μs之间,完全满足工控需求。

总结一下:内核裁剪的核心是“只保留你需要的”,实时补丁的核心是“让内核可抢占”,Buildroot的核心是“最小化”。这三步走完,你的工控网关就有了一个干净、实时、可靠的基础平台。