一、主栈与任务栈(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段]
启动时的关键操作:
- 从Flash中加载
.data
段的初始值到RAM。 - 将
.bss
段对应的RAM区域清零。 - 初始化堆栈指针(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_*.s
或Reset_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
全局变量、字符串常量)。
- Code (
- 存储位置:Flash(程序烧录时写入,运行时直接从Flash执行)。
Total RW Size (RW Data + ZI Data)
- 含义:读写(Read-Write)部分的总大小,包括 已初始化的全局变量 和 未初始化的全局变量。
- 组成:
- RW Data (
.data
段):已初始化的全局/静态变量(如int x = 5;
)。 - ZI Data (
.bss
段):未初始化的全局/静态变量(如static int y;
)。
- RW Data (
- 存储位置: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)。
- Code + RO Data:同
- 存储位置: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)。