MCU堆栈问题

发布于:2025-06-20 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、主栈与任务栈(ucos_ii)

启动文件中的stack EQU0xXXXXXXXX指的是主栈MSP的大小、MSP主要用于系统初始化与中断处理的。(比如中断处理函数中定义一个局部变量,那么这个局部变量就存放在主栈中;以及在裸机系统中定义的局部变量都存在主栈中)。

而在RTOS中在任务中存放的局部变量是放在任务栈中的PSP的,也就是.bss段。

另外,PSP溢出会导致程序崩溃,PSP溢出不一定会使程序崩溃。

二、MCU烧录程序到flash中,执行的时候从flash会移到RAM,没有MMU,那么flash的大小要比RAM小吗?

场景 Flash 大小 vs RAM 大小 说明
​常规执行(XIP)​ Flash 可远大于 RAM 代码在 Flash 中直接运行,仅少量数据/函数需驻留 RAM(如堆栈、全局变量)
​全代码复制到 RAM​ Flash ≤ RAM 极少见,通常仅用于​​极端性能优化​​(如时间关键代码)
1.需要复制到RAM中执行的都有哪些数据?

 数据段(Data Section)​

  • ​初始化的全局变量​​(.data 段):
    编译时存储在 Flash 中,启动时由 startup_*.s 文件中的初始化代码复制到 RAM。
  • ​未初始化的全局变量​​(.bss 段):
    启动时清零对应的 RAM 区域。
  • 内存区域 存储位置 说明
    ​栈(Stack)​ RAM 存放局部变量、函数调用时的返回地址、寄存器上下文等
    ​堆(Heap)​ RAM 动态分配的内存(如malloc/free操作)
  • ​为什么必须在RAM中?​
    • ​栈​​:需要频繁读写(如局部变量、函数调用),而Flash的写入速度极慢且寿命有限。
    • ​堆​​:动态内存分配要求可随机修改,Flash无法满足此需求。

Memory Map:
  Flash (0x08000000): [代码(.text) | 只读数据(.rodata) | 初始化数据(.data的初始值)]
  RAM   (0x20000000): [栈(Stack) | 堆(Heap) | .data段 | .bss段]

 启动时的关键操作​​:

  1. 从Flash中加载.data段的初始值到RAM。
  2. .bss段对应的RAM区域清零。
  3. 初始化堆栈指针(SP)到RAM的栈顶地址。

所以

  • 默认情况下(XIP模式)​​:STM32 的 Flash 大小​​不受 RAM 限制​​,代码直接在 Flash 中执行。
  • ​仅需保证​​:RAM 容量 > (全局变量 + 堆栈 + 需常驻 RAM 的代码段)
  • ​设计建议​​:优先利用 XIP 特性,仅对性能关键部分使用 RAM 执行。

2..bss段不占空间,在运行的时候才分配大小的吗?

首先需要明确的是,.bss段在编译链接时不占用Flash的存储空间​​.bss段中的​未初始化全局/静态变量​​在编译后的二进制文件中​仅记录大小信息​,不存储实际数据(因为默认值为0/NULL)。

例如:static int buffer[1024]; 会在.bss段中标记需要4096字节(假设int为4字节),但不会在Flash中占用4KB空间。

但是在执行时占用RAM的地址空间​​:链接脚本(.ld文件)会为.bss段分配RAM地址范围,但此时​物理RAM尚未被占用​​。运行时由启动代码动态清零​:在startup_*.sReset_Handler()中,系统会​将.bss段对应的RAM区域全部初始化为0​​。此时.bss段才真正占用物理RAM​​。

阶段 Flash占用 RAM占用 说明
​编译链接​ ❌ 不占用 ❌ 不占用 仅记录大小和地址范围
​运行时​ ❌ 不占用 ✅ 实际占用 由启动代码动态分配并清零

3.keil中static了一个数组(初始化/未初始化),超过了RAM空间大小,编译时会报错吗

在 Keil(MDK-ARM)中,如果使用 ​static​ 声明一个数组并超过芯片的 ​​RAM 空间大小​​,​​编译时会报错​​。以下是具体分析:


​编译和链接阶段的检查​

Keil 的编译链接过程会严格检查内存分配,具体表现如下:

  • ​编译阶段​​:static 数组的声明会被正确解析,但不会立即报错(因为尚未分配实际地址)。
  • ​链接阶段​​:链接器(ARM Linker)会根据 ​.map 文件​​ 和 ​​芯片的 RAM 容量​​ 检查所有变量(包括 .data.bss、堆栈等)的总和。
    • ​如果 static 数组(属于 .bss 或 .data 段) + 其他变量 > RAM 大小​​,链接器会直接报错,例如:
Error: L6406E: No space in execution regions with .ANY selector matching xxx.o(.bss).
  • ​错误类型​​:通常是 L6406E(空间不足)或 L6407E(区域溢出)。
4..map中的数值信息
  • RW-data.data 段占用的 RAM(初始化的静态变量)。
  • ZI-data.bss 段占用的 RAM(未初始化的静态变量)。
  • RO-Data:编译后存储在Flash的.rodata段(与代码段.text相邻)(常量枚举等数据。


​ Total RO Size (Code + RO Data)

  • ​含义​​:只读(Read-Only)部分的总大小,包括 ​​可执行代码​​ 和 ​​只读数据​​。
  • ​组成​​:
    • ​Code (.text段)​​:函数代码、中断向量表等。
    • ​RO Data (.rodata段)​​:常量数据(如const全局变量、字符串常量)。
  • ​存储位置​​:​​Flash​​(程序烧录时写入,运行时直接从Flash执行)。

Total RW Size (RW Data + ZI Data)

  • ​含义​​:读写(Read-Write)部分的总大小,包括 ​​已初始化的全局变量​​ 和 ​​未初始化的全局变量​​。
  • ​组成​​:
    • ​RW Data (.data段)​​:已初始化的全局/静态变量(如int x = 5;)。
    • ​ZI Data (.bss段)​​:未初始化的全局/静态变量(如static int y;)。
  • ​存储位置​​:​​RAM​​(运行时使用)。
  • ​关键点​​:
    • RW Data在Flash中存储​​初始值​​,启动时拷贝到RAM。
    • ZI Data不占用Flash,仅记录大小,启动时由代码清零对应RAM区域。

Total ROM Size (Code + RO Data + RW Data)

  • ​含义​​:烧录到Flash(ROM)中的总数据大小,包括 ​​代码、只读数据​​ 和 ​​RW Data的初始值​​。
  • ​组成​​:
    • ​Code + RO Data​​:同Total RO Size
    • ​RW Data的初始值​​:.data段变量在Flash中的备份(启动时需加载到RAM)。
  • ​存储位置​​:​​Flash​​。
  • ​与Total RO Size的区别​​:
    • Total ROM Size ≥ Total RO Size,因为前者额外包含RW Data的初始值。

Flash (ROM) 占用: [ Code ] + [ RO Data ] + [ RW Data的初始值 ]
                |--- Total RO Size ---|           |
                |-------- Total ROM Size --------|

RAM 占用:      [ RW Data ] + [ ZI Data ]
                |---- Total RW Size ----|

 为什么RW Data既占Flash又占RAM?​

  • ​Flash​​:存储变量的初始值(如int x = 5;中的5)。
  • ​RAM​​:运行时变量的实际存储位置(启动时从Flash拷贝初始值到RAM)。

网站公告

今日签到

点亮在社区的每一天
去签到