4、Linux内核改造:内核裁剪、实时性增强(PREEMPT_RT补丁)、设备树修改

好,咱们进入正题。这一章讲的是Linux内核改造,说白了就是给Linux“瘦身”和“提速”。你想想看,一个标准的Linux内核,功能多到吓人,但很多我们用不上。在嵌入式系统里,尤其是要和RTOS混搭的场景,内核必须精简,还得有实时性。

我个人习惯,拿到一个项目,先不急着写代码。先问自己三个问题:

  • 这个系统需要哪些驱动?
  • 实时性要求有多高?
  • 硬件资源(内存、Flash)够不够?

这三个问题想清楚了,内核改造的方向就明确了。

4.1 内核裁剪:去掉你不需要的

内核裁剪,不是让你乱删代码。而是通过配置选项,把不需要的功能模块关掉。我见过不少新手,一上来就make menuconfig,然后一顿乱选,结果编译出来的内核要么跑不起来,要么缺驱动。

我的建议是:先确定你的硬件平台,再确定你的应用场景。比如,你用的是ARM Cortex-A系列,那x86相关的驱动统统可以关掉。你不需要文件系统?那把EXT4、Btrfs都去掉。

常用的裁剪手段:

  • 关闭不用的架构支持:比如你只用ARM,那把x86、MIPS、RISC-V都关掉。
  • 关闭不用的驱动:网卡、声卡、显卡,只留你硬件上有的。
  • 关闭调试选项:比如DEBUG_FS、PRINTK_TIME,这些在正式产品里可以关掉。
  • 关闭不用的文件系统:只保留你需要的,比如只保留EXT4和TMPFS。

重要提示:裁剪不是越狠越好。我曾经在一个项目里,为了省空间,把内核的模块加载功能也关了。结果后来发现,某个驱动必须用模块方式加载,搞得我不得不重新编译整个内核。嗯,这个坑我替你们踩过了。

裁剪后的内核,体积可以从几十MB降到几MB。我做过一个项目,裁剪后内核只有2.3MB,跑在8MB的Flash上,绰绰有余。

4.2 实时性增强:PREEMPT_RT补丁

Linux默认不是实时系统。它的调度延迟可能达到几十毫秒,这对工业控制、机器人来说,完全不能忍。怎么办?打PREEMPT_RT补丁。

PREEMPT_RT补丁,说白了就是把Linux内核变成一个“可抢占”的内核。什么意思?就是高优先级的任务可以随时打断低优先级的任务,哪怕后者正在内核态执行。

安装步骤:

  1. 下载对应内核版本的PREEMPT_RT补丁。注意,版本号必须完全匹配,差一个字母都不行。
  2. 解压内核源码,进入目录。
  3. 打补丁:patch -p1 < patch-xxx-rt.patch
  4. 配置内核:make menuconfig,找到“Preemption Model”,选择“Fully Preemptible Kernel (RT)”
  5. 编译安装。

小技巧:打补丁前,最好先备份原始内核源码。我习惯用git管理,打补丁前先commit一次,万一出问题,git checkout回来就行。

打完补丁后,内核的调度延迟可以降到几十微秒级别。我在一个电机控制项目里测试过,中断响应时间从原来的5ms降到了80μs,效果非常明显。

但要注意,PREEMPT_RT不是万能的。它主要解决的是内核态抢占问题。如果你的应用程序本身写得不好,比如频繁调用系统调用、大量使用自旋锁,那实时性还是会受影响。

警告:PREEMPT_RT补丁会增加内核的开销。因为抢占点多了,上下文切换也更频繁。如果你的CPU性能不强,反而可能降低整体吞吐量。所以,实时性和吞吐量之间,你得做个权衡。

4.3 设备树修改:让内核认识你的硬件

设备树(Device Tree),是Linux用来描述硬件的一种方式。以前用的是板级文件(board file),但那个太死板,换一个硬件就得改代码。设备树就好多了,它把硬件信息独立出来,内核启动时解析设备树,就知道有哪些外设、怎么配置。

设备树的文件后缀是.dts,编译后变成.dtb。修改设备树,其实就是修改.dts文件。

常见的修改场景:

  • 修改内存大小:比如你的板子有512MB内存,但设备树里只配了256MB,那就得改。
  • 添加或修改外设:比如你要用SPI接口的ADC,那就得在设备树里添加SPI节点,并指定ADC的型号和引脚。
  • 修改引脚复用:同一个引脚,可能既可以用作UART,也可以用作GPIO。设备树里要明确指定。

举个例子,假设你要添加一个I2C设备:

&i2c1 {
    status = "okay";
    clock-frequency = <100000>;

    temp_sensor: tmp102@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
    };
};

这段代码的意思是:在I2C1总线上,挂一个TI的TMP102温度传感器,地址是0x48。内核启动后,会去匹配compatible字段,找到对应的驱动。

我在项目中遇到过一个问题:设备树里配的引脚和实际电路对不上。结果内核启动时,驱动加载失败,外设完全没反应。查了两天才发现,原来是设备树里把GPIO号写错了。嗯,从那以后,我每次改设备树,都会对着原理图一个一个引脚核对。

核心要点:设备树修改,一定要和硬件原理图、芯片手册对照着看。别想当然,也别偷懒。一个引脚配错,可能整个系统都起不来。

修改完设备树后,用dtc工具编译:

dtc -I dts -O dtb -o myboard.dtb myboard.dts

然后把生成的.dtb文件放到启动分区里,内核启动时就会加载它。

4.4 三者之间的关系

内核裁剪、PREEMPT_RT补丁、设备树修改,这三件事不是孤立的。它们共同决定了你的系统能不能跑起来、跑得快不快。

改造项 主要目的 常见坑点
内核裁剪 减小体积、降低资源占用 裁剪过度导致功能缺失
PREEMPT_RT 提升实时性、降低调度延迟 增加开销、可能降低吞吐量
设备树修改 适配硬件、正确描述外设 引脚配置错误、地址不匹配

我个人习惯,先做设备树修改,确保硬件能正常工作。然后做内核裁剪,把不需要的功能去掉。最后打PREEMPT_RT补丁,做实时性测试。这个顺序,能让你少走很多弯路。

好了,这一章的内容就这些。下一章,我们会讲如何把改造后的Linux内核和RTOS整合到一起,实现真正的混合部署。到时候,你会看到Linux和RTOS如何共享CPU、内存和外设,那才是真正的硬核内容。