分散加载是一种实现特定代码快速启动的技术,经过优先加载特定代码到内存,达到缩短从系统开机到特定代码执行的时间。可被应用来实现关键业务的快速启动。前端
嵌入式系统经过uboot加载flash上的镜像文件到内存并执行,而镜像文件自己可能较大,因为flash读取速度的限制,将镜像所有加载完再执行可能没法知足时间敏感的业务对启动速度的要求。linux
分散加载的思想是先加载部分镜像并执行,这部分镜像包含了时间敏感的关键业务,从而达到快速启动关键业务的效果。app
Huawei LiteOS的分散加载less
Huawei LiteOS的分散加载分为两个阶段,第一阶段经过uboot将关键业务部分镜像加载到内存并执行,待这部分业务获得执行后,第二阶段在代码中加载剩余部分镜像到内存继续执分散加载的内部原理图如图2所示,图中的运做顺序可参照图1的流程说明。经过合理布局镜像,第一阶段加载部分镜像的速度会比加载完整镜像快,从而缩短系统启动到关键业务运行的时间。函数
在IPC Huawei LiteOS版本上,经过应用分散加载技术,实现了1s内从开机启动到录制,超越Linux版本的3s-4.5s。工具
分散加载的主体思想是将部分时间敏感的业务提早加载执行,具体手段是将与这些业务相关的数据、代码段布局到镜像文件的前端,第一阶段只加载前端这段镜像,达到最短期内便可运行时间敏感业务的开发指导目的。布局
在这些业务获得执行以后,第一阶段的代码中调用分散加载接口加载剩余部分镜像,接着运行镜像剩余部分的业务。命令行
分散加载的内部原理图如图2所示,图中的运做顺序可参照图1的流程说明。debug
分散加载在关键业务第一时间被加载执行以后,再加载非关键业务。code
分散加载技术应用的典型场景是快速启动对时间敏感的业务。
嵌入式系统中可能存在某些业务对启动时间要求比较高,譬如Huawei LiteOS IPC项目上对从开机到录制预览的时间要求较高,能够利用分散加载技术实现录制预览业务的快速启动。
Huawei LiteOS系统中的分散加载模块为用户提供以下接口。
功能分类 | 接口名 | 描述 |
---|---|---|
分散加载接口 | LOS_ScatterLoad | 在分散加载阶段的最后调用此接口,从镜像加载剩余非紧急业务 |
分散加载流程图以下所示。
步骤1 调用接口LOS_ScatterLoad,编写分散加载业务代码
业务代码入口为函数app_init,该函数位于os_adapt.c。在紧急业务代码后调用LOS_ScatterLoad函数进行分散加载,并用#ifndef MAKE_SCATTER_IMAGE、 #endif将该函数后的非紧急业务包围起来,用以编译紧急镜像和所有镜像时做区分,示例代码以下:
void app_init() { proc_fs_init(); hi_uartdev_init(); system_console_init("/dev/uartdev-0"); LOS_CppSystemInit((unsigned long)&__init_array_start__, (unsigned long)&__init_array_end__, BEFORE_SCATTER); LOS_ScatterLoad(0x100000, flash_read, NAND_READ_ALIGN_SIZE); #ifndef MAKE_SCATTER_IMAGE /* 如下为非紧急业务 */ LOS_CppSystemInit((unsigned long)&__init_array_start__, (unsigned long)&__init_array_end__, AFTER_SCATTER); extern unsigned int osShellInit(void); osShellInit(); rdk_fs_init(); SDK_init(); hi_product_driver_init(); char *apszArgv[3]={"vs_server","./higv.bin","-i"}; vs_server(3, apszArgv); #endif /* MAKE_SCATTER_IMAGE */ }
os_adapt.c位于Huawei_LiteOS代码包的platform/bsp/hi3516a/os_adapt路径下。
步骤2 配置SCATTER_SRC变量
在根目录下Makefile中配置SCATTER_SRC,将变量定义为调用分散加载函数的业务源文件路径,以下所示,其中LITEOSTOPDIR指代Huawei_LiteOS代码根目录。
SCATTER_SRC := $(LITEOSTOPDIR)/platform/bsp/$(LITEOS_PLATFORM)/os_adapt/os_adapt.c
步骤3 执行make scatter,编译紧急部分镜像
在根目录下执行以下命令,则不会编译#ifndef MAKE_SCATTER_IMAGE如下的业务代码。编译系统将自动调用工具链抽取分散加载最小镜像的符号表并根据该符号表提取分散加载最小镜像的.a库列表。
Huawei_LiteOS$ make scatter
步骤4 执行make,编译所有镜像
Huawei_LiteOS$ make
编译后,命令行界面会返回紧急镜像大小信息,以下图所示。
查看分散加载连接脚本.text段,新增了scatter.o(.text),以下图所示,实现了将分散加载的快速启动部分代码相关符号归拢到一个同一个段中。
分散加载连接脚本路径:Huawei_LiteOS/tools/scripts/ld/scatter.ld
步骤5 执行tftp 0x82000000 vs_server.bin;nand erase 0x100000 0x700000;nand write 0x82000000 0x100000 0x700000;,将所有镜像烧写到Flash
进入串口工具界面,输入以下命令,将所有镜像烧写到Flash的0x100000地址位。
tftp 0x82000000 vs_server.bin;nand erase 0x100000 0x700000;nand write 0x82000000 0x100000 0x700000;
其中, vs_server.bin为系统镜像文件名,先将其烧写到内存中一段高地址位0x82000000。而后烧写到Flash,起始地址为0x100000,烧写长度为0x700000,即烧写的镜像文件大小不能超过7M,跟据实际镜像大小调整数值。
步骤6 执行nand read 0x80008000 0x100000 0x4E0000; go 0x80008000;,加载紧急业务
执行以下命令,从Flash的0x100000地址处读取长度为0x4E0000的镜像,加载紧急业务到0x80008000。
nand read 0x80008000 0x100000 0x4E0000; go 0x80008000;
步骤7 系统自动重启
系统自动重启,在0x80008000地址处加载镜像。
本节介绍使用分散加载技术遇到的主要问题和解决方法。
arm-hisiv300-linux-ld: cannot find libscatter.O make: *** [vs_server] Error 1
这个问题出现的缘由是修改了连接脚本后,没有对应生成.O文件,解决的方法是生成对应的.O文件而且放到目标目录下
/usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/out/lib/libar6003.a(ar6000_drv.o): In function `ar6000_avail_ev': /usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/vendor/ar6k3_wifi/AR6003/host/qca/source/ ar6000_drv.c:1553: undefined reference to `wireless_init_event' /usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/out/lib/libar6003.a(drv_config.o): In function `ar6000_tkip_micerr_event': /usr1/xxxxx/gerrit_code/modify-debug/liteos_ipc/vendor/ar6k3_wifi/AR6003/host/qca/source/ drv_config.c:1856: undefined reference to `wireless_send_event' make: *** [vs_server] Error 1
这个问题的出现是比较常见的,多是裁剪过程当中在修改连接脚本的时候,将一些必要的.a文件也删除了,这时须要用grep指令在out/lib目录下搜索未定义的变量,找出都存在于哪些.a文件中,将未添加的.a文件添加到连接脚本中。