一、引言:一道面试题引发的思考
在嵌入式开发面试中,一个经典问题是:“请说明程序运行时,代码、全局变量、局部变量分别存储在什么地方?” 这个问题看似简单,却涉及编译、链接、硬件架构等深层知识。本文将深入剖析嵌入式系统中Flash与RAM的内存布局,帮你彻底理解这一核心概念。
二、核心结论:一张图看懂内存布局
首先给出最核心的结论:嵌入式系统的内存布局围绕Flash和RAM的分工协作展开:
· Flash(闪存):相当于"硬盘",负责存储程序代码、常量和数据的初始值 · RAM(内存):相当于"工作车间",负责程序运行时的数据处理和临时存储

三、详细分解:各内存段的作用与位置
1. 代码段 (Text Segment / .text)
· 存储位置:Flash · 内容:程序代码编译后的机器指令 · 特点:只读、大小固定
2. 只读数据段 (Read-Only Data / .rodata)
· 存储位置:Flash · 内容:const常量、字符串常量 · 特点:只读、大小固定
3. 数据段 (Data Segment / .data)
· 存储特点:双向存储 · 初始值存储在Flash · 运行时变量本身在RAM · 内容:已初始化且初始值不为0的全局变量和静态变量 · 启动流程:上电后,启动代码将初始值从Flash拷贝到RAM
4. BSS段 (Block Started by Symbol / .bss)
· 存储位置:RAM · 内容:未初始化或初始值为0的全局变量和静态变量 · 启动流程:上电后,启动代码将整段RAM区域清零
5. 堆 (Heap)
· 存储位置:RAM · 内容:动态分配的内存(malloc/new) · 特点:手动管理、大小动态变化、向高地址生长
6. 栈 (Stack)
· 存储位置:RAM · 内容:局部变量、函数参数、返回地址 · 特点:自动管理、大小固定、向低地址生长
四、链接脚本:内存布局的"总设计师"
链接脚本(Linker Script, .ld文件)是指定内存布局的蓝图,它回答了两个关键问题:
1. 把什么东西(Input sections) → .text, .data等段 2. 放在哪里(Output sections和Memory regions) → Flash还是RAM的具体地址
链接脚本示例片段:
```ld MEMORY { FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K /* 定义Flash区域 */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K /* 定义RAM区域 */ } SECTIONS { .text : { *(.text*) } > FLASH /* 代码段放入Flash */ .rodata : { *(.rodata*) } > FLASH /* 只读数据放入Flash */ /* 数据段:加载地址在Flash,运行地址在RAM */ .data : AT(ADDR(.text) + SIZEOF(.text)) { _sdata = .; *(.data*); _edata = .; } > RAM /* BSS段:放在RAM中 */ .bss : { _sbss = .; *(.bss*); _ebss = .; } > RAM } ```
五、实际案例:ESP32的启动输出分析
当我们烧录一个简单的Hello World程序到ESP32后,串口输出如下:
Hello world! This is ESP32 chip with 2 CPU core(s), WiFi/BT/BLE, silicon revision 1, 2MB external flash Minimum free heap size: 295104 bytes
这段输出告诉我们:
1. 程序运行成功:代码已正确从Flash加载并执行 2. 硬件识别正常:ESP32及其外设被正确初始化 3. 内存充裕:约288KB的空闲堆空间,为后续开发留足余地
六、总结与记忆技巧
段名 存储介质 内容 生命周期 分配方式 .text Flash 程序代码(指令) 永久 编译时 .rodata Flash 常量、字符串 永久 编译时 .data Flash→RAM 已初始化(非0)的全局/静态变量 整个程序 编译时 .bss RAM 未初始化(或初始为0)的全局/静态变量 整个程序 编译时 Heap RAM 动态分配的数据 手动分配和释放 运行时 Stack RAM 局部变量、函数参数 函数调用期间 运行时
记忆口诀:
· Flash存指令和常量,RAM跑数据和变量 · .data有值需拷贝,.bss清零在启动 · 堆往上长栈往下,链接脚本管全家
七、思考题
1. 为什么const常量存储在Flash中,而不是RAM? 2. 如果全局变量初始值为0,放在.data段还是.bss段更节省空间? 3. 堆和栈之间的空间如果发生重叠,会导致什么后果?
希望本文能帮助你彻底理解嵌入式系统的内存布局。如果有任何问题或补充,欢迎在评论区留言讨论!