0
点赞
收藏
分享

微信扫一扫

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

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的全局变量和静态变量
    在这里插入图片描述

注意

  • 测试函数要调用外部变量或函数,不然函数会被编译器优化,这样即使用volatile关键字定义的变量也无法在map文件中查看。
  • Keil对程序进行了优化,未初始化为初始化为0的全局变量和静态变量(结构体、数组)只有内存大于8字节时才会存放在bss段,否则存放在data段!
  • bss段变量必须至少要有1个被调用,才可以在map文件中查看。
  • 局部静态变量只有被调用才能在map文件查看到。

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

举报

相关推荐

0 条评论