跳转至

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中加载好kerneldtb,不需要经过uboot,然后直接跳转到kernel运行,可节省一定的时间。

也可以开启双线/四线/DMA/Cache等优化储存器读取速度。

一般来说 boot0 占用的时间不多,基本不需要对其进行优化

Uboot启动优化

uboot 主要功能是引导内核、量产升级、电源管理、开机音乐/logo、fastboot刷机等。

提高 CPU 以及 flash 读取频率

可设置 uboot-board.dtssys_config.fex 中的 [target]boot_clock 来修改 uboot 运行时CPU频率(注:不能超过SPEC最大频率)。

对于 spinor/spinand,使用较高的时钟频率(一般是100M),使用四线模式或者双线模式(看硬件是否支持),提高加载速度。

关闭串口输出

可将 uboot-board.dtssys_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_normalboot_recovery 变量。

需要根据不同的内核镜像格式来设置不同的值

假设 kernelload address0x40008000

如果使用的是 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,即 androidkernel 格式,其头部大小为 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运算速度、存储器类型、内核压缩及尺寸、根文件系统类型及尺寸、主应用等的不同,优化结果会有一定差异,请以实际优化结果为准。