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 内存分布结构

  • 64KB Flash(用于存放 .text.rodata
  • 20KB RAM(用于 .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 起始)
    

    注意

    1. .text.rodata 只能存放在 Flash(代码和只读常量)。
    2. .data 段在 Flash 里存有初值,但运行时会被复制到 RAM
    3. .bss不会占用 Flash,但会在 RAM 里初始化为 0
    4. 栈和堆共享 RAM,栈 Stack 从 高地址 0x20005000 向低地址 方向增长,堆 Heap 从 低地址 0x20000000 向高地址 方向增长
    5. 当 Stack 和 Heap 彼此增长到一个临界点(即二者相遇),就会导致 Stack Overflow(栈溢出)。

    2.ARM 编译器中的存储字段

    ​ 在 Keil 或 ARM GCC 编译器的 Program Size 统计信息中,列出了四个关键字段:

  • Code(.text)程序代码
  • RO-data(.rodata)只读数据
  • RW-data(.data)已初始化的全局/静态变量
  • ZI-data(.bss)未初始化的全局/静态变量
  • 示例:Keil 编译器的存储统计
    请添加图片描述

    ​ 上图显示了,该程序的内存统计信息如下:

  • Code=13996 byte
  • RO-data=312 byte
  • RW-data=156 byte
  • ZI-data=12156 byte
  • 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)

  • 存储内容:存放程序指令,即 CPU 执行的代码。
  • 存储位置:通常存放在 Flash(ROM),因为代码是固定的,不需要在运行时修改。
  • 特点
  • MCU 复位后,程序从 Flash 读取指令并执行。
  • 代码段通常是只读的,防止意外修改。
  • C 语言的函数和中断服务函数都存放在这里。
  • 示例:
  • void func(void) {  // func 的指令代码存放在 .text 段
        // do something
    }
    

    3.2 .rodata 段 (存储 RO-data,Read-Only Data)

  • 存储内容:存放只读数据,如 const 修饰的全局变量、字符串常量等。
  • 存储位置:通常存放在 Flash,因为这些数据不会被修改。
  • 特点
  • 只能被读取,不能修改。
  • 对于资源有限的 MCU,尽量让数据存放在 Flash,而不是占用 RAM(不需要改变的常量可以用 const 修饰)。
  • 示例
  • const char message[] = "Hello, World!";
    

    3.3 .data 段(存储 RW-data,Read-Write Data )

    存储内容:存放已初始化的全局变量/静态变量

    存储位置

  • 编译时:数据存放在 Flash。
  • 程序运行时:MCU 启动代码(Startup 文件)会在程序启动时,将 .data 段的数据从 Flash 复制到 RAM,确保变量可读写。
  • 所以已初始化的全局变量会同时占用 Flash 和 RAM 的内存空间

  • 特点

  • 变量必须初始化,否则存放在 .bss 段。
  • 既占用 Flash 又占用 RAM(启动时复制)。
  • 示例:

  • int a = 10;    // 存放在 .data 段(Flash + RAM)
    static int b = 20;  // 也存放在 .data 段(Flash + RAM)
    

    3.4 .bss 段(存储 ZI-data,Zero Initialized Data ,)

  • 存储内容:存放未初始化的全局变量和静态变量
  • 存储位置:直接存放在 RAM。
  • 特点
  • 上电后,系统会自动清零(由 Startup 代码完成)。
  • 变量未初始化时占用 RAM 但不会占用 Flash,因为 Flash 里没有初始值。
  • 示例
  • 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 修饰全局变量:

  • 让编译器将变量放入 Flash(节省 RAM)。
  • 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

    作者:电科周杰伦

    物联沃分享整理
    物联沃-IOTWORD物联网 » MCU内存架构深度解析:Flash、RAM及代码与变量存储详解——.rodata、.text、heap、stack、.bss、.data、code与RO-data、RW-data、ZI-data探究

    发表回复