相关阅读
嵌入式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 Totals
5282 292 362 28 1900 513189 ELF Image Totals
5282 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 sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
102 10 0 4 0 1787 delay.o
144 12 0 0 0 879 led.o
108 10 0 0 0 452503 main.o
64 26 304 0 1536 920 startup_stm32f103xe.o
154 32 0 9 0 6279 stm32f1xx_hal.o
272 22 0 0 0 28702 stm32f1xx_hal_cortex.o
424 10 0 0 0 1215 stm32f1xx_hal_dma.o
844 40 0 0 0 2664 stm32f1xx_hal_gpio.o
1708 84 0 0 0 4336 stm32f1xx_hal_rcc.o
796 6 0 0 0 6287 stm32f1xx_hal_uart.o
32 0 0 0 0 3762 stm32f1xx_it.o
102 0 0 0 0 699 sys.o
2 0 24 4 0 1295 system_stm32f1xx.o
176 28 0 7 264 3541 usart.o
----------------------------------------------------------------------
4938 280 362 28 1800 514869 Object Totals
0 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 Name
8 0 0 0 0 68 __main.o
0 0 0 0 0 0 __rtentry.o
12 0 0 0 0 0 __rtentry2.o
6 0 0 0 0 0 __rtentry4.o
52 8 0 0 0 0 __scatter.o
26 0 0 0 0 0 __scatter_copy.o
28 0 0 0 0 0 __scatter_zi.o
18 0 0 0 0 80 exit.o
6 0 0 0 0 152 heapauxi.o
2 0 0 0 0 0 libinit.o
2 0 0 0 0 0 libinit2.o
2 0 0 0 0 0 libshutdown.o
2 0 0 0 0 0 libshutdown2.o
8 4 0 0 96 68 libspace.o
78 0 0 0 0 80 rt_memclr_w.o
2 0 0 0 0 0 rtexit.o
10 0 0 0 0 0 rtexit2.o
74 0 0 0 0 80 sys_stackheap_outer.o
2 0 0 0 0 68 use_no_semi.o
2 0 0 0 0 68 use_no_semi_2.o
----------------------------------------------------------------------
344 12 0 0 100 664 Library Totals
4 0 0 0 4 0 (incl. Padding)
// 例4的.map文件
Image component sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
102 10 0 4 0 1787 delay.o
144 12 0 0 0 879 led.o
108 10 12 4 0 452755 main.o
64 26 304 0 1536 920 startup_stm32f103xe.o
154 32 0 9 0 6279 stm32f1xx_hal.o
272 22 0 0 0 28702 stm32f1xx_hal_cortex.o
424 10 0 0 0 1215 stm32f1xx_hal_dma.o
844 40 0 0 0 2664 stm32f1xx_hal_gpio.o
1708 84 0 0 0 4336 stm32f1xx_hal_rcc.o
796 6 0 0 0 6287 stm32f1xx_hal_uart.o
32 0 0 0 0 3762 stm32f1xx_it.o
102 0 0 0 0 699 sys.o
2 0 24 4 0 1295 system_stm32f1xx.o
176 28 0 7 264 3541 usart.o
----------------------------------------------------------------------
4940 280 372 32 1800 515121 Object Totals
0 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 Name
8 0 0 0 0 68 __main.o
0 0 0 0 0 0 __rtentry.o
12 0 0 0 0 0 __rtentry2.o
6 0 0 0 0 0 __rtentry4.o
52 8 0 0 0 0 __scatter.o
26 0 0 0 0 0 __scatter_copy.o
28 0 0 0 0 0 __scatter_zi.o
18 0 0 0 0 80 exit.o
6 0 0 0 0 152 heapauxi.o
2 0 0 0 0 0 libinit.o
2 0 0 0 0 0 libinit2.o
2 0 0 0 0 0 libshutdown.o
2 0 0 0 0 0 libshutdown2.o
8 4 0 0 96 68 libspace.o
78 0 0 0 0 80 rt_memclr_w.o
2 0 0 0 0 0 rtexit.o
10 0 0 0 0 0 rtexit2.o
74 0 0 0 0 80 sys_stackheap_outer.o
2 0 0 0 0 68 use_no_semi.o
2 0 0 0 0 68 use_no_semi_2.o
----------------------------------------------------------------------
344 12 0 0 96 664 Library Totals
4 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 sizes
Code (inc. data) RO Data RW Data ZI Data Debug Object Name
102 10 0 4 0 1787 delay.o
144 12 0 0 0 879 led.o
120 12 0 4 0 452703 main.o
64 26 304 0 1536 920 startup_stm32f103xe.o
154 32 0 9 0 6279 stm32f1xx_hal.o
272 22 0 0 0 28702 stm32f1xx_hal_cortex.o
424 10 0 0 0 1215 stm32f1xx_hal_dma.o
844 40 0 0 0 2664 stm32f1xx_hal_gpio.o
1708 84 0 0 0 4336 stm32f1xx_hal_rcc.o
796 6 0 0 0 6287 stm32f1xx_hal_uart.o
32 0 0 0 0 3762 stm32f1xx_it.o
102 0 0 0 0 699 sys.o
2 0 24 4 0 1295 system_stm32f1xx.o
176 28 0 7 264 3541 usart.o
----------------------------------------------------------------------
4950 282 362 32 1800 515069 Object Totals
0 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 Name
8 0 0 0 0 68 __main.o
0 0 0 0 0 0 __rtentry.o
12 0 0 0 0 0 __rtentry2.o
6 0 0 0 0 0 __rtentry4.o
52 8 0 0 0 0 __scatter.o
26 0 0 0 0 0 __scatter_copy.o
28 0 0 0 0 0 __scatter_zi.o
18 0 0 0 0 80 exit.o
6 0 0 0 0 152 heapauxi.o
2 0 0 0 0 0 libinit.o
2 0 0 0 0 0 libinit2.o
2 0 0 0 0 0 libshutdown.o
2 0 0 0 0 0 libshutdown2.o
8 4 0 0 96 68 libspace.o
78 0 0 0 0 80 rt_memclr_w.o
2 0 0 0 0 0 rtexit.o
10 0 0 0 0 0 rtexit2.o
74 0 0 0 0 80 sys_stackheap_outer.o
2 0 0 0 0 68 use_no_semi.o
2 0 0 0 0 68 use_no_semi_2.o
----------------------------------------------------------------------
344 12 0 0 96 664 Library Totals
4 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段组成。