MCU内存架构深度解析:Flash、RAM及代码与变量存储详解——.rodata、.text、heap、stack、.bss、.data、code与RO-data、RW-data、ZI-data探究
1. MCU 内存概述
1.1 MCU 内存计算
以 STM32F103CBT6
为例,在 Keil 中可以看见,该款型号 MCU 的 Flash, RAM 大小为:
可以看出 Flash 的地址范围为 0x08000000 ~ 0x08010000,大小为 0x10000(164 byte = 216 byte,所以为 26 KB = 64 KB),同理 RAM 的地址范围为 0x20000000 ~ 0x20005000,大小为 0x5000(5 * 163 byte = 5 * 212 byte,所以为 5 * 22 KB = 20 KB)
1.2 内存分布结构
.text
和 .rodata
).data
、.bss
、栈和堆)程序的典型内存分布:
stm32 由于是 32 位地址总线,所以地址位用八位十六进制数表示(168 = 232)。
pgsql复制编辑+------------------+ 0x08010000 (Flash 结束)
| .rodata | 只读数据(Flash)
+------------------+
| .text | 代码段(Flash)
+------------------+ 0x08000000 (Flash 起始)
+------------------+ 0x20005000 (RAM 结束)
| Heap | 动态分配(malloc)
+------------------+
| Stack | 栈(局部变量、返回地址)
+------------------+
| .bss | 未初始化数据(RAM)
+------------------+
| .data | 已初始化全局变量(RAM)
+------------------+ 0x20000000 (RAM 起始)
注意:
.text
和.rodata
只能存放在 Flash(代码和只读常量)。.data
段在 Flash 里存有初值,但运行时会被复制到 RAM。.bss
段不会占用 Flash,但会在 RAM 里初始化为 0。- 栈和堆共享 RAM,栈 Stack 从 高地址 0x20005000 向低地址 方向增长,堆 Heap 从 低地址 0x20000000 向高地址 方向增长。
- 当 Stack 和 Heap 彼此增长到一个临界点(即二者相遇),就会导致 Stack Overflow(栈溢出)。
2.ARM 编译器中的存储字段
在 Keil 或 ARM GCC 编译器的 Program Size 统计信息中,列出了四个关键字段:
示例:Keil 编译器的存储统计
上图显示了,该程序的内存统计信息如下:
3. 存储区域的详细解析
MCU 代码和变量在存储时会被划分到不同的区域,各个区域用途如下:
存储区域 | 用途 | 存储位置 |
---|---|---|
.text(Code) | 存放编译后的程序指令(代码) | Flash(只读) |
.rodata(RO-data) | 存放只读常量(const 变量等) |
Flash(只读) |
.data(RW-data) | 已初始化的全局/静态变量 | Flash + RAM |
.bss(ZI-data) | 未初始化的全局/静态变量,启动时清零 | RAM(自动清零) |
Heap(堆) | malloc() 动态分配的内存 |
RAM(向上增长) |
Stack(栈) | 局部变量、函数调用返回地址 | RAM(向下增长) |
3.1 .text段(存储 code)
void func(void) { // func 的指令代码存放在 .text 段
// do something
}
3.2 .rodata 段 (存储 RO-data,Read-Only Data)
const
修饰的全局变量、字符串常量等。const char message[] = "Hello, World!";
3.3 .data 段(存储 RW-data,Read-Write Data )
存储内容:存放已初始化的全局变量/静态变量。
存储位置:
.data
段的数据从 Flash 复制到 RAM,确保变量可读写。所以已初始化的全局变量会同时占用 Flash 和 RAM 的内存空间
特点:
.bss
段。示例:
int a = 10; // 存放在 .data 段(Flash + RAM)
static int b = 20; // 也存放在 .data 段(Flash + RAM)
3.4 .bss 段(存储 ZI-data,Zero Initialized Data ,)
int global_var; // 存放在 .bss 段,自动初始化为 0
static int static_var; // 也是 .bss 段,自动初始化为 0
3.4 堆(Heap)和栈(Stack)
除了上述四个标准段,MCU 运行时还包含 堆(Heap) 和 栈(Stack),它们都位于 RAM,但用法不同。
存储区域 | 作用 | 增长方向 |
---|---|---|
堆(Heap) | 存放 malloc() 动态分配的数据 |
向上增长 |
栈(Stack) | 存放局部变量、函数调用返回地址 | 向下增长 |
示例:
void foo(void) {
int local_var = 10; // local_var 在栈(stack)
int *ptr = malloc(10); // malloc 分配的内存在堆(heap)
}
4. 代码优化建议
4.1 尽可能使用 const
修饰全局变量:
const int lookup_table[] = {1, 2, 3, 4}; // 存在 Flash,而不是 RAM
4.2 避免全局变量初始化为 0
.bss
,减少 Flash 和 RAM 的同时占用。int a = 0; // 额外占用 Flash
int b; // 更节省空间,存放在 .bss,自动初始化为 0
4.3 避免 malloc(),使用静态分配
malloc()
,嵌入式系统中 RAM 资源有限,推荐使用静态数组。// 避免动态分配,改为静态数组
int buffer[256];
5. 总结
Code(.text):存放程序代码(存储在 Flash)。
RO-data(.rodata):存放只读数据(存储在 Flash)。
RW-data(.data):存放已初始化的全局变量(Flash 复制到 RAM)。
ZI-data(.bss):存放未初始化的全局变量(RAM,上电后自动清零)。
Heap(堆):malloc()
动态分配内存(RAM)。
Stack(栈):存放局部变量、函数调用(RAM)。
ROM (Flash) size = Code + Ro-data + rw-data;
RAM size = Rw-data + zi-data
作者:电科周杰伦