单片机的存储空间与内存分区
ROM
、RAM
、FLASH
与内存分区
一、物理存储介质
ROM(Read-Only Memory)
- 只读存储器- 特性:非易失性存储器,内容出厂固化,不可修改(现代系统中常被
FLASH
替代)。 - 用途:存储
BootLoader
、固定配置数据。
- 特性:非易失性存储器,内容出厂固化,不可修改(现代系统中常被
FLASH
- 特性:非易失性存储器、可擦写。
- 用途:存储程序代码(
.text
)、常量(.rodata
)、中断向量表。
RAM(Random Access Memory)
- 随机存取存储器- 特性:易失性存储器、高速读写,断电数据丢失。
- 用途:存储运行时数据(堆、栈、全局变量、动态内存)。
二、程序内存分区
- 代码区(
.text
段)- 位置:
FLASH
中。 - 内容:编译后的机器指令(函数代码)、字符串常量也有可能存放在该区。
- 示例:见文末。
- 位置:
- 只读数据(常量)区(
.rodata
段)- 位置:
FLASH
中。 - 内容:
const
修饰的全局/静态常量、字符串。 - 示例:见文末。
- 位置:
- 全局区/静态存储区
- 全局区有
.bss
段和.data
段组成,可读可写;位与RAM
中。 - 位置:
.bss
段 - 启动时清零- 未初始化的全局变量、初始化为0的全局变量、初始化为0的静态变量。
.bss
段不占用可执行文件空间,启动时由操作系统清零。
.data
段 - 初始值从FLASH
加载- 已经初始化的全局变量、已经初始化的静态变量(
static
修饰)。 .data
段占用可执行文件空间,其内容由程序(程序员)初始化。
- 已经初始化的全局变量、已经初始化的静态变量(
- 示例:见文末。
- 全局区有
- 栈区(
Stack
)- 位置:
RAM
中,由编译器自动管理。 - 内容:函数内临时创建的局部变量、函数参数、函数返回值、
const
定义的局部变量。 - 特点:
LIFO
(后进先出),大小固定(可能溢出)。 - 示例:见文末。
- 位置:
- 堆区(
Heap
)- 位置:
RAM
中,位于.bss
段末尾和栈区之间。 - 内容:动态分配的内存(
malloc
/free
)。 - 特点:手动管理,需防止内存泄漏。
- 示例:见文末。
- 位置:
IAR
与KEIL
输出的文件信息
IAR:
1 | MAP文件信息: |
KEIL:
1 | 1. Build Output输出信息: |
三、协作流程与联系
- 程序启动阶段
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
上电后从固定地址(如STM32
的0x08000000
)读取向量表,若重映射需配置SCB->VTOR
。
七、附录
数据存放位置示例
1 |
|
参考文章
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 龙猫知识库!