[012] [RT-Thread学习笔记] 程序内存分布

RT-Thread
学习笔记
各段含义
测试代码
可执行映像文件
启动搬运流程

RT-Thread版本:4.0.5
MCU型号:STM32F103RCT6(ARM Cortex-M3 内核)

1 各段含义

一般 MCU 包含的存储空间有:片内 Flash (ROM)与片内 RAM。

    Total RO  Size (Code + RO Data)    
    Total RW  Size (RW Data + ZI Data)  >>> RAM     
    Total ROM Size (Code + RO Data + RW Data) 	>>> Flash(ROM)
Section含义内存地址分配阶段属性
Code存放函数体的二进制代码编译时分配只读
RO-data(Read Only data)常量(局部常量在栈区)编译时分配只读
RW-data(Read Write data)初值非0的全局变量和静态变量编译时分配读写
ZI-data(Zero Initialie data)未初始化或初始化为0的全局变量和静态变量编译时分配读写
ZI-data空间(Stack)局部变量(局部静态变量除外运行时分配读写
ZI-data空间(Heap)使用malloc动态分配的空间运行时分配读写

MDK的map文件可以查看RO段、RW段和部分ZI段的变量与函数的起始地址、大小等,ZI段的堆栈区中的局部变量是无法查看的,因为它们的内存地址是运行时分配的。

2 测试代码

struct test{
    int a;
    char b;
    short c;
    const char* p;
};

volatile const char* s1 = "hello world";     // 常量 RO
volatile int g_buf1[100] = {1};   // 初值非0的全局数组	RW
volatile char g_buf2[9];          // 初值为0的全局数组	ZI (bss)
volatile char g_buf3[8];          // 初值为0的全局数组	ZI (data)
volatile int g_a1 = 1;	          // 初值非0的全局变量	RW
volatile char g_a2;	              // 未初始化的全局变量	ZI
volatile static int s_a1 = 2;	  // 初始化的全局静态变量	RW
volatile static int s_a2;	      // 未初始化的全局静态变量	ZI

volatile int* g_ptr1;             // 未初始化的全局指针变量	ZI      
struct test ss_t;                 // 未初始化的全局结构体变量	ZI (bss)     

// int *ptr1 = rt_malloc(4 * sizeof(int));	// 直接报错
int main(void)  // Code
{	
    volatile static int s_a3[3] = {0};  // 初始化为0的局部静态数组 ZI   (bss)
    volatile static int s_a4[4] = {1};  // 初始化的局部静态数组 RW  (data)
    volatile static int s_a5;           // 未初始化的局部静态变量 ZI
    volatile static int s_a6 = 2;       // 初始化的局部静态变量	RW
    const char c1 = 't';                // 局部常量	ZI-Stack
	volatile int a1;		            // 局部变量	ZI-Stack
	volatile int *ptr2 = rt_malloc(4 * sizeof(int));	// 局部变量	ZI-heap, ptr2指针指向的内存空间位于动态内存堆空间中
    
    // bss段变量必须至少要有1个被调用,才能显示
    g_buf2[0] = 3;
    /* 局部静态变量只有被调用才能在map文件查看到 */
    rt_kprintf("%d, %d, %d, %d", s_a3[0], s_a4, s_a5, s_a6);
    return 0;
}

volatile关键字防止未使用的变量被编译器优化。

map文件相关内容如下:

data段:
	全局变量和常量:
    s1                                       0x20000000   Data           4  main.o(.data)
    g_buf1                                   0x20000004   Data         400  main.o(.data)
    g_buf3                                   0x20000194   Data           8  main.o(.data)
    g_a1                                     0x2000019c   Data           4  main.o(.data)
    g_a2                                     0x200001a0   Data           1  main.o(.data)
    g_ptr1                                   0x200001ac   Data           4  main.o(.data)
    静态变量:
    s_a1                                     0x200001a4   Data           4  main.o(.data)
    s_a2                                     0x200001a8   Data           4  main.o(.data)
    s_a4                                     0x200001b0   Data          16  main.o(.data)
    s_a5                                     0x200001c0   Data           4  main.o(.data)
    s_a6                                     0x200001c4   Data           4  main.o(.data)
bss段(mdk优化,大于8字节才进bss):
	未初始化的全局变量:
    g_buf2                                   0x200004c4   Data           9  main.o(.bss)
    ss_t                                     0x200004d0   Data          12  main.o(.bss)
	未初始化的静态变量:
	s_a3                                     0x200004dc   Data          12  main.o(.bss)

堆栈变量无法查看,运行时才分配内存。

关于text、data、bss说明

  • text:存放程序执行代码,只读。
  • data:存放已初始化的全局变量和静态变量、全局常量
  • bss:即ZI-data(不含堆栈),存放未初始化或初始化为0的全局变量和静态变量
    在这里插入图片描述

注意

  • Keil对程序进行了优化,未初始化为初始化为0的全局变量和静态变量(结构体、数组)只有内存大于8字节时才会存放在bss段,否则存放在data段
    官方给出的解释ARMCC: Variables Get Located To RW Rather Than ZI
    在这里插入图片描述
    可以在Options for Target - C/C++ - Misc Controls通过--bss_threshold=n(n为bss段优化阈值)修改。
  • 测试函数要调用外部变量或函数,不然函数会被编译器优化,这样即使用volatile关键字定义的变量也无法在map文件中查看。
  • 当前.C文件中bss段和data段变量分别必须至少要有1个被调用,不然相应段中变量都会被编译器优化。
  • 局部静态变量只有被调用才不会被优化。

3 可执行映像文件启动搬运流程

可执行映像文件(烧录文件)的内存分布如下图(图左)所示,主要包含RO段和RW段部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
在这里插入图片描述

STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 段的起始地址和大小分配出 ZI 段,并将这块 RAM 区域清零(由用户程序或编译器提供的一些库函数初始化成零,这样做节省ROM空间)。

RTT的动态内存堆即为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。

END

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西的彷徨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值