相关阅读
嵌入式https://blog.csdn.net/weixin_45791458/category_12768532.html
// 例1
int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */delay_init(72); /* 初始化延时函数 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);}
}
假设以例1所示的程序为例,在Keil编译它后,编译信息会有如下所示的提示。
Program Size: Code=5282 RO-data=362 RW-data=28 ZI-data=1900
在项目映射文件的底部,也可以找到类似的信息。
==============================================================================Code (inc. data) RO Data RW Data ZI Data Debug 5282 292 362 28 1900 513189 Grand Totals5282 292 362 28 1900 513189 ELF Image Totals5282 292 362 28 0 0 ROM Totals==============================================================================Total RO Size (Code + RO Data) 5644 ( 5.51kB)Total RW Size (RW Data + ZI Data) 1928 ( 1.88kB)Total ROM Size (Code + RO Data + RW Data) 5672 ( 5.54kB)==============================================================================
本文就将详细阐述Keil的Code、RW、RO、ZI段。
Code段
通俗来讲,Code段存储了指令,因为程序的指令在运行过程中通常不需要修改,这个段通常是只读的,所以被存放在非易失性存储器中(ROM、Flash等)。
例2在例1的基础上增加了一个新的函数并调用了它。
void func(void)
{LED0(0); /* LED0 亮 */return;
}int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */delay_init(72); /* 初始化延时函数 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */func();LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);}
}
此时的编译结果如下所示,可以看到除了Code段有增加,其他段都没有变化。
Program Size: Code=5310 RO-data=362 RW-data=28 ZI-data=1900
由于现在很多单片机采用XIP的方式执行程序,因此不再需要将Code段的代码从非易失性存储器搬到RAM中进行执行了。
RW段
RW段存储了可读写数据,存放有初始值且没有const关键词修饰的全局变量和静态变量,这些变量的值需要保存在非易失性存储器中,在程序启动时会从非易失性存储器复制到RAM中(这个过程由Code段内的一些初始化函数完成),并且在程序运行时可以读写。
需要特别注意的是,即使是函数内的静态变量也是保存在RW段的,这代表着即使函数还未被调用,它内部的静态变量就已经存在了。
例3在例1的基础上增加了一个有初始值的全局变量和静态变量(没有const关键词修饰)。
// 例3
int a __attribute__((used)) = 11111;
int main(void)
{static int b __attribute__((used)) = 11111;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */delay_init(72); /* 初始化延时函数 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);;}
}
__attribute__((used))属性强制告诉编译器,这个变量必须保留,即使它没有被显式使用。此时的编译结果如下所示,可以看到RW段增加了8,也就是两个int类型数据所占用的空间。
Program Size: Code=5282 RO-data=362 RW-data=36 ZI-data=1900
RO段
RO段存储了只读数据,存放有初始值且被const关键词修饰的全局变量和有初始值且被const关键词修饰、用指针指向的字符串字面量、printf/scanf函数中的格式字符串。这些值需要保存在非易失性存储器中,在程序启动时会从非易失性存储器复制到RAM中(这个过程由Code段内的一些初始化函数完成),并且在程序运行时只读。
例4在例1的基础上增加了一个有初始值且被const关键词修饰的全局变量和静态变量,一个指针指向的字符串字面量。
// 例4
const int a __attribute__((used)) = 11111;
char * str __attribute__((used)) = "123";
int main(void)
{const static int b __attribute__((used)) = 11111;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */delay_init(72); /* 初始化延时函数 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);;}
}
此时的编译结果如下所示,但是RO段只增加了10而不是预计的12,且Code段增加了2,它应该不变才对。
Program Size: Code=5284 RO-data=372 RW-data=32 ZI-data=1896
要弄清楚为什么会有这种情况,需要去看项目映射文件,下面列出了例1和例4的.map文件。
// 例1的.map文件
Image component sizesCode (inc. data) RO Data RW Data ZI Data Debug Object Name102 10 0 4 0 1787 delay.o144 12 0 0 0 879 led.o108 10 0 0 0 452503 main.o64 26 304 0 1536 920 startup_stm32f103xe.o154 32 0 9 0 6279 stm32f1xx_hal.o272 22 0 0 0 28702 stm32f1xx_hal_cortex.o424 10 0 0 0 1215 stm32f1xx_hal_dma.o844 40 0 0 0 2664 stm32f1xx_hal_gpio.o1708 84 0 0 0 4336 stm32f1xx_hal_rcc.o796 6 0 0 0 6287 stm32f1xx_hal_uart.o32 0 0 0 0 3762 stm32f1xx_it.o102 0 0 0 0 699 sys.o2 0 24 4 0 1295 system_stm32f1xx.o176 28 0 7 264 3541 usart.o----------------------------------------------------------------------4938 280 362 28 1800 514869 Object Totals0 0 32 0 0 0 (incl. Generated)10 0 2 4 0 0 (incl. Padding)----------------------------------------------------------------------Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name8 0 0 0 0 68 __main.o0 0 0 0 0 0 __rtentry.o12 0 0 0 0 0 __rtentry2.o6 0 0 0 0 0 __rtentry4.o52 8 0 0 0 0 __scatter.o26 0 0 0 0 0 __scatter_copy.o28 0 0 0 0 0 __scatter_zi.o18 0 0 0 0 80 exit.o6 0 0 0 0 152 heapauxi.o2 0 0 0 0 0 libinit.o2 0 0 0 0 0 libinit2.o2 0 0 0 0 0 libshutdown.o2 0 0 0 0 0 libshutdown2.o8 4 0 0 96 68 libspace.o78 0 0 0 0 80 rt_memclr_w.o2 0 0 0 0 0 rtexit.o10 0 0 0 0 0 rtexit2.o74 0 0 0 0 80 sys_stackheap_outer.o2 0 0 0 0 68 use_no_semi.o2 0 0 0 0 68 use_no_semi_2.o----------------------------------------------------------------------344 12 0 0 100 664 Library Totals4 0 0 0 4 0 (incl. Padding)
// 例4的.map文件
Image component sizesCode (inc. data) RO Data RW Data ZI Data Debug Object Name102 10 0 4 0 1787 delay.o144 12 0 0 0 879 led.o108 10 12 4 0 452755 main.o64 26 304 0 1536 920 startup_stm32f103xe.o154 32 0 9 0 6279 stm32f1xx_hal.o272 22 0 0 0 28702 stm32f1xx_hal_cortex.o424 10 0 0 0 1215 stm32f1xx_hal_dma.o844 40 0 0 0 2664 stm32f1xx_hal_gpio.o1708 84 0 0 0 4336 stm32f1xx_hal_rcc.o796 6 0 0 0 6287 stm32f1xx_hal_uart.o32 0 0 0 0 3762 stm32f1xx_it.o102 0 0 0 0 699 sys.o2 0 24 4 0 1295 system_stm32f1xx.o176 28 0 7 264 3541 usart.o----------------------------------------------------------------------4940 280 372 32 1800 515121 Object Totals0 0 32 0 0 0 (incl. Generated)12 0 0 4 0 0 (incl. Padding)----------------------------------------------------------------------Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name8 0 0 0 0 68 __main.o0 0 0 0 0 0 __rtentry.o12 0 0 0 0 0 __rtentry2.o6 0 0 0 0 0 __rtentry4.o52 8 0 0 0 0 __scatter.o26 0 0 0 0 0 __scatter_copy.o28 0 0 0 0 0 __scatter_zi.o18 0 0 0 0 80 exit.o6 0 0 0 0 152 heapauxi.o2 0 0 0 0 0 libinit.o2 0 0 0 0 0 libinit2.o2 0 0 0 0 0 libshutdown.o2 0 0 0 0 0 libshutdown2.o8 4 0 0 96 68 libspace.o78 0 0 0 0 80 rt_memclr_w.o2 0 0 0 0 0 rtexit.o10 0 0 0 0 0 rtexit2.o74 0 0 0 0 80 sys_stackheap_outer.o2 0 0 0 0 68 use_no_semi.o2 0 0 0 0 68 use_no_semi_2.o----------------------------------------------------------------------344 12 0 0 96 664 Library Totals4 0 0 0 0 0 (incl. Padding)
从中可以看出,main.c编译得到的目标文件main.o中code段大小是108,没有改变,而RO段确实从0变成了12。问题在于,例4的Code段的(incl. Padding)从10变成了12,因此造成了整体的Code段增加了2,例4的RO段的(incl. Padding)从2变成了0,因此造成了整体的RO段只增加了10。
ZI段
ZI段存储了读写数据,存放没有初始值的全局变量和有初始值且被const关键词修饰。这些值不需要保存在非易失性存储器中,在程序启动时会直接在RAM中初始化为0(这个过程由Code段内的一些初始化函数完成)。
例5在例1的基础上增加了一个无初始值的全局变量。
// 例5
int b __attribute__((used));
int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */delay_init(72); /* 初始化延时函数 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);}
}
奇怪的是,似乎编译器将无初始值的全局变量放在了RW段,如下所示(这点尚不明确,希望有懂的大佬告诉我)。
// 例5的.map文件
Image component sizesCode (inc. data) RO Data RW Data ZI Data Debug Object Name102 10 0 4 0 1787 delay.o144 12 0 0 0 879 led.o120 12 0 4 0 452703 main.o64 26 304 0 1536 920 startup_stm32f103xe.o154 32 0 9 0 6279 stm32f1xx_hal.o272 22 0 0 0 28702 stm32f1xx_hal_cortex.o424 10 0 0 0 1215 stm32f1xx_hal_dma.o844 40 0 0 0 2664 stm32f1xx_hal_gpio.o1708 84 0 0 0 4336 stm32f1xx_hal_rcc.o796 6 0 0 0 6287 stm32f1xx_hal_uart.o32 0 0 0 0 3762 stm32f1xx_it.o102 0 0 0 0 699 sys.o2 0 24 4 0 1295 system_stm32f1xx.o176 28 0 7 264 3541 usart.o----------------------------------------------------------------------4950 282 362 32 1800 515069 Object Totals0 0 32 0 0 0 (incl. Generated)10 0 2 4 0 0 (incl. Padding)----------------------------------------------------------------------Code (inc. data) RO Data RW Data ZI Data Debug Library Member Name8 0 0 0 0 68 __main.o0 0 0 0 0 0 __rtentry.o12 0 0 0 0 0 __rtentry2.o6 0 0 0 0 0 __rtentry4.o52 8 0 0 0 0 __scatter.o26 0 0 0 0 0 __scatter_copy.o28 0 0 0 0 0 __scatter_zi.o18 0 0 0 0 80 exit.o6 0 0 0 0 152 heapauxi.o2 0 0 0 0 0 libinit.o2 0 0 0 0 0 libinit2.o2 0 0 0 0 0 libshutdown.o2 0 0 0 0 0 libshutdown2.o8 4 0 0 96 68 libspace.o78 0 0 0 0 80 rt_memclr_w.o2 0 0 0 0 0 rtexit.o10 0 0 0 0 0 rtexit2.o74 0 0 0 0 80 sys_stackheap_outer.o2 0 0 0 0 68 use_no_semi.o2 0 0 0 0 68 use_no_semi_2.o----------------------------------------------------------------------344 12 0 0 96 664 Library Totals4 0 0 0 0 0 (incl. Padding)
最后来说一下文章开头提到的项目映射文件的底部,项目整体的RO Size、RW Size和ROM Size。
- RO Size是非易失存储器中保存只读数据的大小,由Code段和RO段组成。
- RW Size是RAM中保存全局数据的大小,由RW段和ZI段组成。这不包括函数内部定义的局部变量,它们保存在寄存器或者堆栈中。
- ROM Size是非易失存储器的最小大小,由Code段、RO段和RW段组成。