摘要:本文会给大家介绍下LiteOS Studio的镜像剖析工具,这可是一个评估、优化镜像文件RAM、ROM占用大小的利器。
大家都晓得嵌入式开发板因为受老本限度,芯片的RAM、Flash等硬件资源无限,比方有些低成本的开发板只有内置的64KB ROM、20KB RAM。在丰盛性能个性编程时,一些看似有害的扭转,都可能导致编译出的镜像收缩,超出开发板的资源限度。对于硬件资源绝对拮据的开发板,正当的镜像大小布局,也会晋升性能。本文会给大家介绍下LiteOS Studio的镜像剖析工具,这可是个评估、优化镜像文件RAM、ROM占用大小的利器。
开发环境筹备
后期的系列文章,咱们把握了如何装置LiteOS Studio,如何新建STM32F769IDISCOVERY和Qemu realview-pbx-a9工程。这次咱们以STM32F429IGTx开发板为例,创立工程的形式是一样的,开发板抉择STM32F429IGTx即可。镜像剖析个性对任何LiteOS工程都是实用的,咱们只是以该开发板为例。工程创立结束后,执行编译,输入如下:
终端控制台输入显示编译胜利了,可执行文件Huawei_LiteOS.elf对应的text段为74774 bytes,data段大小1444 bytes,bss段13080 bytes,dec示意后面三段大小的共计,为89268bytes。这些text、data、bss数据代表什么?有什么意义?咱们持续,后文中会具体解释。
LiteOS 镜像剖析个性
点击LiteOS Studio工具栏的调测工具Debug Tools按钮,关上调试工具,抉择镜像剖析,这就是本文要给大家介绍的LiteOS Studio的镜像剖析工具。填写可执行文件门路、Map文件门路等,如图:
点击确定按钮,会主动关上镜像剖析窗口。蕴含内存区域、详细信息、文件大小、模块大小等4个选项卡。咱们顺次演示如何应用。
- 内存区域
内存区域页面评估剖析LiteOS开发板工程对内存的细分应用状况。对于STM32F429IGTx开发板,显示的内存区域region蕴含FLASH、RAM、CCMRAM,展现的信息蕴含每个内存区域的名称、起始内存地址、总大小、闲暇大小、已用大小,应用比例。在这个内存区域页面,除了数值展现剖析,还提供饼图能够宏观的评估每个区域的应用、残余状况。如下图所示,FLASH总大小 1024Kb,RAM总大小192Kb,对FLASH、RAM的使用率较低,刚刚超7%。对于CCMRAM更是没有应用,CCM(Core Coupled Memory)是给STM32F4内核专用的全速64KB RAM。
- 详细信息
持续点击详细信息选项卡关上镜像剖析详细信息页面,该页面展现每个内存区域蕴含的内存段section,内存段蕴含的符号symbol的详细信息。 比方FLASH上面蕴含.isr_vector、.text、.rodata等内存段, 内存段又蕴含调配在该段的程序符号。每一行展现的信息蕴含运行地址VMA(Virtual Memory Address)、装载地址LMA(Load Memory Address)、内存段/符号的大小。其中,LMA示意程序装载的内存地址;VMA示意程序运行时的内存地址。嵌入式零碎中RAM内存空间无限,个别把程序放在FLASH中,LMA地址为Flash中的地址,等到程序运行时,再载入到RAM中的运行地址VMA。内存段.data、.vector_ram就属于这种状况,VMA、LMA地址不一样,并且在FLASH、RAM均呈现。
在应用详情页面,反对排序和搜寻过滤,反对程序符号疾速跳转到源代码行。咱们能够很直观的评估每个内存段、程序符号的应用大小状况,能够疾速定位到潜在可优化的资源耗费小户。
如图:
从镜像剖析图表中,能够看出text、data段寄存在Flash存储,data、bss段数据寄存在RAM存储。那么,链接器linker是怎么晓得如何把各个段的符号数据寄存在ROM、RAM的?这就要靠链接脚本。
链接脚本和Text、Data、BSS Section段
链接脚本蕴含一些规定来束缚链接器如何把函数、变量放到ROM或RAM,STM32F429IGTx工程的链接脚本地位在targetsCloud_STM32F429IGTx_FIREliteos.ld。咱们分栏同时展现镜像剖析页面和链接脚本,如下图,能够看出镜像剖析页面展现的内存段应用状况和链接脚本中所定义的是统一的,开发板的内存应用状况在LiteOS Studio 镜像剖析页面失去十分直观的展现。
内存段.ccmram在链接脚本中没有定义,已应用大小的也为0 byte。对于内存段.data、.vector_ram,链接脚本中应用关键字 RAM AT> FLASH,来示意装载地址、理论运行地址别离在FLASH、RAM。
链接脚本片段1如下,定义了中断解决向量.isr_vector、程序.text寄存在Flash存储,应用的关键字为>FLASH,其中FLASH在上文的MEMORY{......}中定义。
/ The startup code goes first into FLASH / .isr_vector :
{
. = ALIGN(4);KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4);
} >FLASH / The program code and other data goes into FLASH / .text :
{
. = ALIGN(4);__text_start = .; *(.text) /* .text sections (code) */*(.text*) /* .text* sections (code) */*(.glue_7) /* glue arm to thumb code */*(.glue_7t) /* glue thumb to arm code */*(.eh_frame)KEEP (*(.init))KEEP (*(.fini)). = ALIGN(4);_etext = .; /* define a global symbols at end of code */ __text_end = _etext;
} >FLASH
链接脚本片段2如下,定义了.vector_ram、.data寄存在Flash存储,在程序启动时,会从Flash复制到RAM。应用的关键字为 >RAM AT> FLASH,其中RAM在上文的MEMORY{}中定义。
/ Initialized liteos vector sections goes into RAM, load LMA copy after code / .vector_ram :
{
. = ORIGIN(RAM);_s_liteos_vector = .; *(.data.vector) /* liteos vector in ram */ _e_liteos_vector = .;
} > RAM AT> FLASH / used by the startup to initialize data / _sidata = LOADADDR(.data); / Initialized data sections goes into RAM, load LMA copy after code / .data ALIGN(0x1000):
{
__ram_data_start = _sdata;. = ALIGN(4);_sdata = .; /* create a global symbol at data start */*(.data) /* .data sections */*(.data*) /* .data* sections */ KEEP(*( SORT (.liteos.table.*)));. = ALIGN(4);_edata = .; /* define a global symbol at data end */ __ram_data_end = _edata;
} >RAM AT> FLASH
链接脚本片段3如下,定义了.bss、._user_heap_stack占用RAM存储。应用的关键字为 >RAM。
.bss :
{ / This is used by the startup in order to initialize the .bss secion / _sbss = .; / define a global symbol at bss start / bss_start = _sbss;
__bss_start = _sbss; *(.bss) *(.bss*) *(COMMON). = ALIGN(4);_ebss = .; /* define a global symbol at bss end */ __bss_end__ = _ebss;__bss_end = _ebss;
} >RAM / User_heap_stack section, used to check that there is enough RAM left / ._user_heap_stack :
{
. = ALIGN(8);PROVIDE ( end = . );PROVIDE ( _end = . );. = . + _Min_Heap_Size;. = . + _Min_Stack_Size;. = ALIGN(8);
} >RAM
当初来总结一下,通常是这样的:
- Text段
Text存储在只读的ROM内存区域,蕴含向量表、程序代码、只读常量数据。
- Data段
示意初始化过的变量。存储在FLASH、RAM。链接器调配data段数据在Flash区域,在startup启动时,从ROM中复制到RAM。ROM中保留的是只读的正本,开发板重启也不会扭转;RAM中保留的是读写正本,在程序运行过程中,变量值可能会变动。
- BSS段
示意未初始化的变量。RAM中未初始化的数据,在程序启动时初始化为0。
- 其余内存段
.user_heap_stack内存堆,malloc()申请内存应用此区域;.ARM.attributes、.debug_frame等存储调试信息。
Map映射文件、ASM反汇编文件
在程序编译链接时须要指定链接脚本,编译胜利后会生成map映射文件,保留程序、数据的内存映射信息。映射文件是比拟重要的辅助剖析文件,能够获取程序中的函数、变量如何调配到RAM、ROM。在应用镜像剖析性能的时候,咱们指定了Map映射文件outCloud_STM32F429IGTx_FIREHuawei_LiteOS.map。反汇编文件是另外一个比拟重要的文件,如果一个函数占用了太多的text代码段、data数据段时,此时能够剖析反汇编代码。
利用镜像剖析个性联合反汇编文件,咱们很不便评估函数是否能够进一步优化改良。以main函数为例,在映射文件中的片段如下, main函数的寄存地址为0x08000ea0,函数占用空间大小0x38 bytes(=56 bytes)。
.text.startup.main 0x08000ea0 0x38 d:/LiteOS_master/out/Cloud_STM32F429IGTx_FIRE/liblibCloud_STM32F429IGTx_FIRE.a(main.o) 0x08000ea0 main
这个数据在LiteOS Studio镜像剖析页面也能够疾速查阅到:
上面是main()函数反汇编片段。能够看出,咱们是C代码和反汇编混合展现的。第一列8000ea0是地址,第二列是汇编指令的机器码、而后是汇编代码。
函数开始地址为0x8000ea0,完结地址为0x8000ed4,函数占用空间大小=0x8000ed4-0x8000ea0+0x4=0x38 bytes。如果函数长度过长,联合剖析反汇编代码行,进行定位优化。
08000ea0 <main>:
main():
d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:45 INT32 main(VOID)
{
8000ea0: b598 push {r3, r4, r7, lr}
8000ea2: af00 add r7, sp, #0 d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:46 HardwareInit();
8000ea4: f7ff ffea bl 8000e7c <HardwareInit> d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:48 PRINT_RELEASE("nHello Huawei LiteOSn" 8000ea8: 4b07 ldr r3, [pc, #28] ; (8000ec8 <main+0x28>)
8000eaa: 4a08 ldr r2, [pc, #32] ; (8000ecc <main+0x2c>)
8000eac: 4908 ldr r1, [pc, #32] ; (8000ed0 <main+0x30>)
8000eae: 4809 ldr r0, [pc, #36] ; (8000ed4 <main+0x34>)
8000eb0: f000 fb84 bl 80015bc <dprintf> d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:54
"nLiteOS Kernel Version : %sn" "build data : %s %snn" "**********************************n", HW_LITEOS_KERNEL_VERSION_STRING, __DATE__, __TIME__);UINT32 ret = OsMain();
8000eb4: f003 fd18 bl 80048e8 <OsMain> d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:55
if (ret != LOS_OK) {
8000eb8: b108 cbz r0, 8000ebe <main+0x1e> d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:56
return LOS_NOK;
8000eba: 2001 movs r0, #1 d:LiteOS_mastertargetsCloud_STM32F429IGTx_FIRE/Src/main.c:62 }
OsStart(); return 0;
}
8000ebc: bd98 pop {r3, r4, r7, pc}
8000ebe: 4604 mov r4, r0
入手试验工夫
咱们入手编码创立些不同类型的变量、函数,看看这些会调配到哪些内存段,理论调配是否合乎咱们已把握的常识。减少下述代码片段到targetsCloud_STM32F429IGTx_FIRESrcuser_task.c文件中的函数UINT32 app_init(VOID)的上方,在该UINT32 app_init(VOID)函数内首行减少对 ABC_FunName(0);的调用。
static UINT32 ABC_static_global_init = 1;
UINT32 ABC_global_init = 1;
UINT32 ABC_global_noInit; const UINT32 ABC_global_const = 1; static VOID ABC_Static_FunName(VOID){
printf("ABC_static_global_init is %d.n", ABC_static_global_init);printf("ABC_global_init is %d.n", ABC_global_init);printf("ABC_global_noInit is %d.n", ABC_global_noInit);printf("ABC_global_const is %d.n", ABC_global_const);
}
UINT32 ABC_FunName(UINT32 ABC_num){
CHAR *ABC_var_inFun = "1234567890";UINT32 ABC_var_inFuc_init = 1; static UINT32 ABC_static_InFuc_init = 1; static UINT32 ABC_static_InFuc_noinit; const UINT32 ABC_inFuc_const = 1;ABC_static_InFuc_noinit = 99;printf("ABC_var_inFuc_init is %d.n", ABC_var_inFuc_init);printf("ABC_static_InFuc_init is %d.n", ABC_static_InFuc_init);printf("ABC_static_InFuc_noinit is %d.n", ABC_static_InFuc_noinit);printf("ABC_inFuc_const is %d.n", ABC_inFuc_const);CHAR *buf = LOS_MemAlloc(m_aucSysMem0, 8);buf[0] = ABC_var_inFun[0];LOS_MemFree(m_aucSysMem0, buf);(VOID)ABC_Static_FunName(); return 0;
}
从新编译,点击镜像剖析页面的刷新按钮从新展现。咱们新增的符号都以ABC_结尾,咱们以这个关键字搜寻一下,能够看进去,ABC_FunName函数属于.text代码段,全局初始化的变量ABC_global_init属于.data段,全局未初始化变量ABC_global_noInit属于.bss段。动态函数ABC_Static_FunName、函数栈没有在这个区域展现。这合乎咱们已把握的常识,如下图,大家也能够自行尝试一下。
本文介绍了嵌入式开发中的内存布局、链接脚本,映射文件,通过实例演示了如何利用LiteOS Studio的镜像剖析个性,如何联合反汇编文件来评估函数空间占用。 LiteOS Studio是咱们LiteOS物联网开发的利器!欢送大家去应用这个个性,并分享应用心得,有任何问题、倡议,都能够留言给咱们https://gitee.com/LiteOS/Lite...。谢谢。
本文分享自华为云社区《应用LiteOS Studio镜像剖析工具评估优化LiteOS镜像 》,原文作者:zhushy 。
点击关注,第一工夫理解华为云陈腐技术~