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节点,全是摆设。
怎么裁剪?我的做法是:
- 先搞清楚产品用了哪些外设
- 对照原理图,只保留对应的节点
- 删除所有
status = "disabled"的节点——既然禁用了,留着干嘛? - 检查时钟和电源节点,去掉未使用的
举个例子,这是裁剪前的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打开内核时间戳,看看每个阶段花了多少时间。然后针对耗时最长的部分下手。这样效率最高。
下一章我们会讲内核启动参数的优化,包括如何去掉不必要的内核模块和驱动。到时候再聊。