嵌入式程序内存分布及启动流程 LMA与VMALMA加载内存地址。指程序段如代码、数据在非易失性存储器中的存储地址通常是Flash/ROM。程序烧录后便存放于此。VMA虚拟内存地址运行地址。指程序段在易失性存储器中的运行地址即CPU执行指令或访问数据时使用的地址通常是RAM。关系程序的生命周期始于LMA存储状态但部分数据必须“搬家”到VMA才能正确运行。启动代码的核心任务就是完成这次“搬迁”。注意并非所有段都需要从LMA拷贝到VMA。有些段如.text的LMA等于VMA可直接执行而有些段如.data则必须拷贝。特点“LMA决定数据存在哪里VMA决定数据在哪里运行。核心程序段.text段---Code段存放所有可执行的机器指令c语言编译后的结果cpu上电后从这里取址执行程序起点只读LMA和VMA通常都设置在Flash中。STM32的Flash支持XIP技术CPU可以直接从Flash读取并执行指令无需将其拷贝到RAM。.rodata段(只读数据段)---RO-data段存放只读常量如用const修饰的全局变量、字符串常量等程序运行时直接读取使用不允许修改尝试修改此段数据会导致硬件错误和.text段一样通常与.text一起常驻FlashLMAVMA无需拷贝CPU直接从Flash读取。.data段已初始化数据段---RW-data段存放已初始化且初值非0的全局变量和静态变量变量在程序运行过程中需要被频繁读写因此必须在高速的RAM中其初始值需要被长久保存而RAM掉电会丢失。因此产生了“分身”机制。初始值作为镜像保存在FlashLMA。变量运行时的空间位于RAMVMA。上电启动时启动代码必须将Flash中的初始值拷贝到RAM中对应的地址。.bss段---ZI-data段存放未初始化或初始化为0的全局变量和静态变量。变量同样需要在RAM中被读写但其初始状态明确为0。不占用固件体积。在ELF格式文件中标记为NOBITS意味着它在Flash中不存储任何有效数据。在FlashLMA中没有存储初始值的镜像。在RAMVMA中预留出对应大小的空间。上电启动时启动代码将这块RAM区域统一清零即可。运行时内存区域堆和栈程序运行起来后用于动态管理的内存区域它们只有VMA在RAM中没有对应的LMA镜像。栈编译器自动管理用于函数调用时保存局部变量、参数、返回地址等后进先出向下增长。分配与回收速度极快。空间通常有限由启动文件配置。栈溢出是嵌入式系统最常见、最隐蔽的错误之一会导致数据被破坏通常表现为HardFault堆用于动态内存分配通过malloc/free等函数手动管理向上增长分配灵活容易产生内存碎片且分配/释放时间不确定。在资源紧张、要求实时性的MCU上必须谨慎使用甚至禁止使用。内存占用以keil编译结果为例Program Size: Code2900 RO-data1788 RW-data12 ZI-data1636总Flash占用 Code RO-data RW-data 2900 1788 12 4690字节总RAM占用 RW-data ZI-data 12 1636 1648内存的逻辑分布图为程序运行时的“虚拟逻辑视图”。在单片机如STM32中Flash 和 RAM 是两个独立的物理硬件它们的地址是各自独立编址的只是被巧妙地映射到了同一个“逻辑地址空间”的不同位置。这张图即运行时变量和代码分别躺在哪个硬件里以及它们在那个硬件里的地址高低关系。非物理地址布局而是 逻辑地址空间Virtual Address Space / Linker View——由链接器Linker统一规划的“虚拟”地址映射视图用于指导程序如何加载和运行。移植Freertos后ram分布如下图启动流程上电启动阶段MCU从Flash执行.text从0x8000000开始执行复位向量启动代码启动代码执行C运行时初始化将Flash中的.data初始值拷贝到RAM的.data区域将.bss区域清零设置栈指针跳入主循环运行时CPU访问0x800xxxx-》Flash只读代码/常量CPU访问0x2000xxxx-》SRAM(可读写全局变量堆栈)栈从高地址向低地址生长堆从低地址向高地址生长