单片机的存储空间与内存分区
ROM、RAM、FLASH与内存分区
一、物理存储介质
ROM(Read-Only Memory)- 只读存储器- 特性:非易失性存储器,内容出厂固化,不可修改(现代系统中常被
FLASH替代)。 - 用途:存储
BootLoader、固定配置数据。
- 特性:非易失性存储器,内容出厂固化,不可修改(现代系统中常被
FLASH- 特性:非易失性存储器、可擦写。
- 用途:存储程序代码(
.text)、常量(.rodata)、中断向量表。
RAM(Random Access Memory)- 随机存取存储器- 特性:易失性存储器、高速读写,断电数据丢失。
- 用途:存储运行时数据(堆、栈、全局变量、动态内存)。
二、程序内存分区
- 代码区(
.text段)- 位置:
FLASH中,只读不可修改。 - 内容:编译后的机器指令(函数代码)。
- 示例:见文末。
- 位置:
- 只读数据区(
.rodata段)(又称常量区)- 位置:
FLASH中,只读不可修改。 - 内容:
const修饰的全局/静态常量、字符串、字符串字面量。 - 示例:见文末。
- 位置:
- 全局区/静态存储区
- 全局区由
.bss(Block Started by Symbol 表示该段由符号(变量名)标记起始位置,存储未初始化的数据块)段和.data段组成,可读可写;位与RAM中。 - 位置:
.bss段 – 启动时由启动代码清零- 未初始化的全局变量、初始化为0的全局变量、初始化为0的静态变量。
- 该段不占用可执行文件空间,启动时由操作系统(启动代码)清零,该段仅记录变量的大小和地址。
.data段 – 初始值由启动代码从FLASH加载,如.rodata段- 已经初始化的全局变量、已经初始化的静态变量(
static修饰)。 .data段占用可执行文件空间,其内容由程序(程序员)初始化。- 程序启动时,启动代码(如
startup_xxx.s)会将这些初始值从Flash拷贝到该段
- 已经初始化的全局变量、已经初始化的静态变量(
- 示例:见文末。
- 全局区由
- 栈区(
Stack)- 位置:
RAM中,由编译器自动管理。 - 内容:函数内临时创建的局部变量、函数参数、函数返回值、
const定义的局部变量、函数调用时的现场保护(如寄存器状态)。 - 特点:
LIFO(后进先出),大小固定(可能溢出)。 - 示例:见文末。
- 位置:
- 堆区(
Heap)- 位置:
RAM中,位于.bss段末尾和栈区之间。 - 内容:动态分配的内存(
malloc/free)。 - 特点:手动管理,需防止内存泄漏。
- 示例:见文末。
- 位置:
IAR与KEIL输出的文件信息
IAR:
1 | MAP文件信息: |
KEIL:
1 | 1. Build Output输出信息: |
1. Code(代码段)
- 存储位置:Flash(ROM)
- 内容:编译器生成的机器指令(程序执行代码)。
- 特性:
- 只读,程序运行时从Flash中直接读取执行。
- 占用Flash空间,不占用RAM。
- 示例:函数体、中断服务程序等。
2. RO-data(只读数据段)
存储位置:Flash(ROM)
内容,程序中定义的常量数据,如:
const修饰的全局变量(如const int sensor_enable = 0xFE;)。- 字符串常量(如
char* msg = "Hello";中的"Hello")。
- 字符串常量(如
特性:
只读,程序运行时不可修改。
- 占用Flash空间,不占用RAM。
示例:传感器配置常量、固定查找表等。
3. RW-data(读写数据段)
存储位置:
编译时:初始值存储在 Flash(ROM) 中。
- 运行时:程序启动后,启动代码(如
startup_xxx.s)会将初始值从Flash拷贝到 RAM 中。
- 运行时:程序启动后,启动代码(如
内容:已初始化的全局变量或静态变量,且初始值非零(如
int global_var = 10;)。特性:
可读写,程序运行时修改的值存储在RAM中。
- 占用Flash空间(存储初始值)和RAM空间(运行时存储变量值)。
示例:全局配置参数、静态计数器等。
4. ZI-data(零初始化数据段)
存储位置:RAM
内容:未初始化的全局变量或静态变量,或显式初始化为零的变量(如
int global_zero = 0;或int global_uninit;)。特性:
程序启动时,启动代码会将该段所在的
RAM区域全部清零(无需从Flash拷贝初始值)。- 不占用Flash空间,仅占用RAM空间。
示例:未初始化的全局缓冲区、静态标志位等。
三、协作流程与联系
- 程序启动阶段
FLASH加载:CPU从FLASH的0x8000000读取中断向量表,执行Reset_Handler。RAM初始化:- 将
FLASH中的.data段初始值复制到RAM。 - 清零
RAM中的.bss段。
- 将
- 堆栈初始化:
- 根据链接脚本设置堆(
_heap_start)和栈(_stack_top)的起始地址。
- 根据链接脚本设置堆(
- 运行时交互
- 代码执行:
CPU从FLASH的.text段读取指令。 - 数据访问:
- 全局变量从
RAM的.data或.bss段读写。 - 常量从
FLASH的.rodata段读取。
- 全局变量从
- 函数调用:
- 局部变量在栈区动态分配。
- 递归调用过深可能导致栈溢出
(Stack Overflow)。
- 动态内存:通过堆区分配,需手动管理生命周期。
- 代码执行:
- 中断处理
- 栈切换:中断触发时,自动使用主栈指针(
MSP)或进程栈指针(PSP)。 - 上下文保存:寄存器和返回地址压入栈区。
- 栈切换:中断触发时,自动使用主栈指针(
四、内存布局图示

五、差异总结
| 区域 | 存储介质 | 内容 | 管理方式 | 生命周期 |
|---|---|---|---|---|
| 代码区 (.text) | FLASH | 程序指令、常量字符串 | 编译器自动分配 | 永久 |
| .rodata | FLASH | 只读全局常量 | 编译器自动分配 | 永久 |
| .data | RAM | 已初始化全局/静态变量 | 启动时从FLASH加载 | 程序运行期 |
| .bss | RAM | 未初始化全局/静态变量 | 启动时清零 | 程序运行期 |
| 堆区 (Heap) | RAM | 动态分配内存 | 手动分配/释放 | 直到free调用 |
| 栈区 (Stack) | RAM | 局部变量、函数上下文 | 编译器自动分配 | 函数调用期间 |
六、常见问题
FLASH和ROM的区别?
- ROM是只读存储器,FLASH是可擦写的非易失存储器,现代嵌入式系统中FLASH取代了ROM。
全局变量和静态变量何时初始化?
- 初始化的全局/静态变量在启动时从FLASH加载到RAM的
.data段;未初始化的在.bss段,启动时清零。
- 初始化的全局/静态变量在启动时从FLASH加载到RAM的
堆和栈溢出如何检测?
- 栈溢出:通过编译器选项(如
GCC的-fstack-protector)或硬件MPU保护。 - 堆溢出:需工具检测(如
Valgrind)或自定义内存管理。
- 栈溢出:通过编译器选项(如
中断向量表为何必须放在FLASH起始地址?
CPU上电后从固定地址(大部分芯片均为0x08000000)读取向量表,若重映射需配置SCB->VTOR。
七、附录
数据存放位置示例
1 |
|
参考文章
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 龙猫知识库!
