Tina Linux 启动优化
启动速度是嵌入式产品一个重要的性能指标,更快的启动速度会让客户有更好的使用体验,在某些方面还会节省能耗,因为可以直接关机而不需要休眠。
启动速度优化可提升产品的竞争力。对于某些系统来说,启动速度是硬性要求。
TinaLinux系统当前的启动流程如下:
brom --> boot0 --> (monitor/secure os) --> uboot --> rootfs --> app
brom
固化在 IC
内部,芯片出厂后就无法更改。
后续将从 boot0
开始分阶段介绍启动优化的方法。
BOOT0 启动优化
boot0
运行在SRAM
,主要功能是对 DRAM
进行初始化,并将 uboot
加载至DRAM
。
boot0
可优化的地方不多,可以做的是:
-
关闭串口输出。
-
减少检测按键和检测串口的等待时间。
-
加载uboot的时候,不要先加载后搬运,直接加载到uboot的运行地址。
对于spinor
的方案,还可以直接从boot0
启动,只需要在boot0
中加载好kernel
和dtb
,不需要经过uboot
,然后直接跳转到kernel
运行,可节省一定的时间。
也可以开启双线/四线/DMA/Cache等优化储存器读取速度。
一般来说 boot0
占用的时间不多,基本不需要对其进行优化
Uboot启动优化
uboot
主要功能是引导内核、量产升级、电源管理、开机音乐/logo、fastboot
刷机等。
提高 CPU
以及 flash
读取频率
可设置 uboot-board.dts
或 sys_config.fex
中的 [target]
下boot_clock
来修改 uboot
运行时CPU频率(注:不能超过SPEC最大频率)。
对于 spinor/spinand
,使用较高的时钟频率(一般是100M),使用四线模式或者双线模式(看硬件是否支持),提高加载速度。
关闭串口输出
可将 uboot-board.dts
或 sys_config.fex
中的 [platform]
下 debug_mode
设置为 0 来关闭 uboot
的串口输出。
可将 sys_config.fex
中的 [platform]
下 debug_mode
设置为 0 来关闭 boot0
串口输出。
配置此项后,如果还有少量输出,有两个可能的原因:
-
第一是这些输出是在获取
debug_mode
流程之前产生。 -
第二是因为源码中直接使用了
puts
而没有使用printf
。
对于这两者情况,需要修改源码来完全关闭串口输出。
修改 kernel 加载位置
如果 uboot
将内核加载到 DRAM
的地址与内核中 load address
不匹配,就需要将内核移动到正确位置,这样会浪费一定的时间。因此,可以直接修改 uboot
加载内核为正确的地址。
具体是修改 env.cfg
文件的 boot_normal
与 boot_recovery
变量。
需要根据不同的内核镜像格式来设置不同的值。
假设 kernel
的 load address
为 0x40008000
。
如果使用的是 uImage
,也就是在kernel的镜像前加了64字节,所以 uboot
应该将 kernel
加载到 0x40008000 - 0x40 = 0x40007fc0
。
#uImage/raw
boot_normal=sunxi_flash read 40007fc0 ${boot_partition};bootm 40007fc0
boot_recovery=sunxi_flash read 40007fc0 recovery;bootm 40007fc0
如果使用的是 boot.img
,即 android
的 kernel
格式,其头部大小为 0x800
,所以 uboot
应该将 kernel
加载到 0x40008000 - 0x800 = 40007800
。
#boot.img/raw
boot_normal=sunxi_flash read 40007800 ${boot_partition};bootm 40007800
boot_recovery=sunxi_flash read 40007800 recovery;bootm 40007800
如果 uboot
加载 kernel
地址与 load address
不匹配,uboot
过程中串口输出可能会有:
Loading Kernel Image ... OK
如果是匹配的,uboot
过程中串口输出可能会有:
XIP Kernel Image ... OK
kernel 启动优化
通常来说,内核启动耗时较多,需要更深入的优化。
kernel 压缩方式
比较不同压缩方式的启动时间和flash占用情况,选择一种符合实际情况的。
此处给出某次测试结果供参考。实际优化的时候,需要重新测试,根据实际情况选择。
压缩方式 | 内核大小/M | 加载时间/s | 解压时间/s | 总时间/s |
---|---|---|---|---|
LZO | 2.4 | 0.38 | 0.23 | 0.61 |
GZIP | 1.9 | 0.35 | 0.44 | 0.79 |
XZ | 1.5 | 0.25 | 2.17 | 2.42 |
加载位置
内核镜像可以由 kernel
自解压,也有 uboot
进行解压的情况。
对于 kernel
自解压的情况,如果压缩过的 kernel
与解压后的 kernel
地址冲突,则会先把自己复制到安全的地方,然后再解压,防止自我覆盖。这就需要耗费复制的时间。
比如对于运行地址为 0x80008000
的内核来说,bootloader
可以将其加载到0x81008000
,当然其他位置也可以。
内核裁剪
裁剪内核,带来的加速是两个方面的。一是体积变小,加载解压耗时减少;二是内核启动时初始化内容变少。
裁剪要根据产品的实际情况来,将不需要的功能及模块都去掉。
具体是执行"make kernel_menuconfig"
,关闭不需要的选项。
initcall 优化
在 cmdline
中设置 initcall_debug=1
,即可打印跟踪所有内核初始化过程中调用 initcall
的顺序以及耗时。
具体修改 env.cfg
配置文件, 新增一行"initcall_debug=1"
,
并在"setargs_*"
后加入" initcall_debug=${initcall_debug}"
,如下所示。
setargs_nand=setenv bootargs console=${console} console=tty0 root=${nand_root} init=${init} loglevel=${loglevel} partitions=${partitions} initcall_debug=${initcall_debug}
加入后,内核启动时就会有类似如下的打印,对于耗时较多的initcall,可进行深入优化。
[ 0.021772] initcall sunxi_pinctrl_init+0x0/0x44 returned 0 after 9765 usecs
[ 0.067694] initcall param_sysfs_init+0x0/0x198 returned 0 after 29296 usecs
[ 0.070240] initcall genhd_device_init+0x0/0x88 returned 0 after 9765 usecs
[ 0.080405] initcall init_scsi+0x0/0x90 returned 0 after 9765 usecs
[ 0.090384] initcall mmc_init+0x0/0x84 returned 0 after 9765 usecs
内核 module
需要考虑启动速度的界定,对于 内核module 的优化主要有两点:
-
对于必须要加载的模块,直接编译进内核
-
对于不急需的功能,可以编译成模块。
比如某个应用,会开启主界面联网,启动速度以出现主界面为准,那么可以考虑将disp编入内核,wifi编译成模块,后续需要时再动态加载。
rootfs 启动优化
initramfs
initramfs是一个内存文件系统,会占用较多DRAM。
部分产品可能会用到initramfs来过渡到rootfs,其优化思路大体与rootfs类似。可参考本节后续的优化方案。
rootfs类型以及压缩
存储介质、文件系统类型,压缩方式对rootfs挂载有很大影响。
此处给出某次测试结果供参考。实际优化的时候,需要重新测试,根据实际情况选择。
类型 | 压缩 | 介质 | 总时间/s |
---|---|---|---|
squashfs | gzip | emmc | 0.12 |
squashfs | xz | emmc | 0.27 |
squashfs | xz | nand | 0.26 |
ext4 | - | emmc | 0.12 |
rootfs 裁剪
文件系统越小,加载速度越快。裁剪的主要思路是:删换压,即删除没有用到的,用小的换大的,选择合适的压缩方式。
指定文件系统类型
内核在挂载rootfs时,会有一个try文件系统类型的过程。可以在cmdline直接指定,节省时间。
具体是在cmdline中添加"rootfstype=<type>"
,其中type为文件系统类型,如ext4、squashfs等。
静态创建dev节点
对于dev下面的节点,事先根据实际情况创建好,而不是在系统启动后动态生成,理论上也可以节省一定的时间。
rootfs拆分
可以将rootfs拆分成两个部分,一个小的文件系统先挂载执行,大的文件系统根据需要动态挂载。
主应用程序启动优化 主应用程序主要是由用户开发,因此主导优化的还是用户,这里提一些优化措施:
-
提升运行顺序。将应用程序放在init很前面执行。
-
动态/静态链接。
-
编译选项。
-
暂时不使用的库采用dlopen方式。
-
应用程序拆分。
开启Tina启动速度优化
在tina根目录下执行make menuconfig使能CONFIG_BOOT_TIME_OPTIMIZATION,具体如下所示
Tina Configuration
└─> Target Images
└─>[*] Boot Time Optimization
注:如果看不到该选项,使用?键搜索,会发现此项有一些依赖选项,使能依赖选项即可看到 Boot Time Optimization
实验结果
在某 norflash
方案上开启 CONFIG_BOOT_TIME_OPTIMIZATION
后,启动速度提升效果如下:
-
Linux内核镜像压缩方式从GZIP换成LZO,优化 > 0.2s。
-
rootfs从squashfs XZ压缩换成squashfs GZIP压缩,优化 > 0.15s。
-
屏蔽boot0、uboot、kernel启动阶段控制台打印,优化 > 2s。
注:对于不同的方案,由于CPU运算速度、存储器类型、内核压缩及尺寸、根文件系统类型及尺寸、主应用等的不同,优化结果会有一定差异,请以实际优化结果为准。