3、内核启动加速:内核压缩与解压策略、DTS裁剪

大家好,我是你们的嵌入式系统讲师。今天我们来聊聊内核启动加速里非常关键的一环——压缩与解压策略,以及DTS裁剪。

说实话,很多工程师把精力都花在优化uboot和文件系统上,却忽略了内核本身。你想想看,内核从加载到解压再到初始化,这个过程占用了整个启动时间的很大比重。我见过不少项目,uboot优化得飞快,结果卡在内核解压上,白白浪费了好几秒。

3.1 内核压缩方式的选择

内核镜像通常都是压缩的。为什么?因为Flash空间太宝贵了。但压缩和解压是一对矛盾体——压缩率越高,镜像越小,但解压时间越长。

常见的压缩算法有这么几种:

压缩方式 压缩率 解压速度 适用场景
gzip 中等 通用场景,最常用
lzo 较低 极快 追求启动速度
lzma Flash空间极度紧张
xz 很高 很慢 极少使用,解压太慢

我个人习惯,在机顶盒项目里首选lzo。为什么?因为机顶盒的Flash通常有128MB甚至更大,空间不是主要矛盾。启动速度才是用户体验的关键。lzo的解压速度比gzip快30%左右,虽然压缩率差一些,但换来的是实实在在的秒级提升。

核心观点:启动速度优先的项目,选lzo;Flash空间优先的项目,选gzip。lzma和xz除非万不得已,否则别碰。

怎么配置呢?在内核配置里找到这里:

General setup  --->
    Kernel compression mode  --->
        (X) LZO

嗯,就这么简单。但要注意,选了lzo之后,你的uboot也得支持lzo解压。我记得有一次帮客户调试,内核配了lzo,结果uboot还是老版本的gzip解压,直接启动失败。这个坑我踩过,大家引以为戒。

3.2 解压位置与解压时机

内核解压的位置也很讲究。默认情况下,内核会解压到内存的某个固定地址。但如果你用的是多核处理器,或者有DMA占用内存,就得小心了。

我建议的做法是:

  • 把解压地址放在内存的高端区域,避开uboot和DMA缓冲区
  • 如果可能,使用CONFIG_RELOCATABLE选项,让内核自己决定解压位置

还有一个技巧——提前解压。什么意思?就是在uboot阶段就把内核解压好,然后直接跳转到解压后的地址。这样内核启动时就不用再解压了。

小技巧:如果Flash空间允许,可以尝试存储未压缩的内核镜像。虽然占用空间大,但省去了解压时间,启动速度能提升0.5-1秒。

不过要注意,未压缩的内核镜像大小通常在5-10MB,对于128MB的Flash来说问题不大。但如果是小容量Flash,还是老老实实用压缩吧。

3.3 DTS裁剪——去掉你不需要的

DTS(Device Tree Source)是内核描述硬件的方式。但很多厂商的DTS都是复制粘贴来的,里面塞了一大堆用不到的节点。

为什么会这样?因为开发板厂商为了兼容性,会把所有可能的硬件都列在DTS里。但你的产品只用到了其中一部分。那些多余的节点,内核启动时都会去初始化,白白浪费时间。

我曾经接手过一个项目,DTS文件有3000多行。仔细一看,里面光I2C设备就定义了20多个,实际上只用了3个。还有一堆GPIO、SPI、UART节点,全是摆设。

怎么裁剪?我的做法是:

  1. 先搞清楚产品用了哪些外设
  2. 对照原理图,只保留对应的节点
  3. 删除所有status = "disabled"的节点——既然禁用了,留着干嘛?
  4. 检查时钟和电源节点,去掉未使用的

举个例子,这是裁剪前的DTS:

&i2c0 {
    status = "okay";
    touch@38 {
        compatible = "goodix,gt911";
        reg = <0x38>;
    };
    /* 实际上没用到的设备 */
    eeprom@50 {
        compatible = "at24,24c02";
        reg = <0x50>;
    };
};

&i2c1 {
    status = "disabled";  /* 这个总线根本没接设备 */
    /* 里面还有一堆子节点,全是浪费 */
};

裁剪后:

&i2c0 {
    status = "okay";
    touch@38 {
        compatible = "goodix,gt911";
        reg = <0x38>;
    };
};
/* i2c1 整个删除,因为没用到 */

警告:裁剪DTS时一定要对照原理图,别把必要的节点删了。我建议先做一次完整的启动日志分析,看看内核初始化了哪些设备,再决定删什么。

裁剪完DTS后,重新编译内核。你会发现内核镜像变小了,启动时间也缩短了。一般来说,每减少100行DTS,启动时间能快50-100毫秒。积少成多,效果很明显。

3.4 实战经验总结

好了,我们来总结一下今天的内容:

  • 压缩策略:追求速度用lzo,追求空间用gzip
  • 解压优化:考虑提前解压或使用未压缩镜像
  • DTS裁剪:只保留必要的硬件节点,删除所有disabled节点

最后说一句,内核启动加速不是一蹴而就的事。我建议你先用printk.time打开内核时间戳,看看每个阶段花了多少时间。然后针对耗时最长的部分下手。这样效率最高。

下一章我们会讲内核启动参数的优化,包括如何去掉不必要的内核模块和驱动。到时候再聊。