当前位置: 首页 > article >正文

单片机内存划分总览与介绍

1 单片机内存划分总览

内存区域

存储内容

特性

FLASH (Text)

代码(.text)、常量(.rodata

只读,掉电不丢失

RAM

.data(已初始化全局变量)

读写,掉电丢失

.bss(未初始化全局变量)

初始化为0,占用RAM空间

堆(Heap)(动态内存分配)

手动分配释放(如malloc

栈(Stack)(局部变量、函数调用)

自动分配释放,大小需预先配置

1. .text 段(代码段)

定义与特性

代码示例

// 代码本身编译后位于.text段

int main() {

return 0;

}

// const常量位于.rodata段(.text的子段)

const uint32_t FIXED_VALUE = 0x12345678; // 存储在FLASH中

关键点

2. .data 段(已初始化数据段)

定义与特性

代码示例

uint32_t global_var = 0xABCD; // 全局变量,位于.data段

void func() {

static int static_var = 100; // 静态局部变量,也位于.data段

}

初始化过程

编译器将初始值写入FLASH的 .data 镜像区。

单片机启动时,启动代码(如 startup_*.s)将FLASH中的初始值复制到RAM的 .data 段。

链接脚本片段

Ld

.data : {

_data_start = .; /* RAM中.data段的起始地址 */

*(.data*) /* 所有.data段内容 */

_data_end = .; /* RAM中.data段的结束地址 */

} > RAM AT > FLASH /* 物理存储在FLASH,运行时在RAM */

3. .bss 段(未初始化数据段)

定义与特性

代码示例

uint32_t bss_var; // 未初始化,位于.bss段

static char buffer[1024]; // 未初始化的静态数组,位于.bss段

int zero_var = 0; // 显式初始化为0,位于.bss段

初始化过程

启动代码将 .bss 段对应的RAM区域清零:

assembly

/* 典型启动代码片段(ARM汇编) */

ldr r0, =_bss_start

ldr r1, =_bss_end

mov r2, #0

clear_bss:

cmp r0, r1

strlt r2, [r0], #4

blt clear_bss

4. 堆(Heap)

定义与特性

代码示例

void demo_heap() {

int* ptr = (int*)malloc(100 * sizeof(int)); // 从堆分配

if (ptr != NULL) {

// 使用内存

free(ptr); // 必须手动释放

}

}

配置堆大小

在链接脚本中定义堆区域:

ld

_heap_start = .; /* 堆起始地址 */

. = . + 0x1000; /* 堆大小为4KB */

_heap_end = .; /* 堆结束地址 */

5. 栈(Stack)

定义与特性

代码示例

void demo_stack() {

int local_var = 42; // 局部变量,位于栈

char buffer[256]; // 大型局部数组,可能引发栈溢出

// ...

}

配置栈大小

在链接脚本中定义栈区域(通常位于RAM顶端):

Ld

_stack_top = ORIGIN(RAM) + LENGTH(RAM); /* 栈顶地址(向下生长) */

_stack_size = 0x800; /* 栈大小为2KB */

. = . - _stack_size; /* 分配栈空间 */

_stack_bottom = .; /* 栈底地址 */

6. 扩展段:.ccmram(紧耦合内存)

定义与特性

代码示例

1

__attribute__((section(".ccmram"))) uint32_t fast_buffer[128]; // 分配到CCM RAM

7. 内存布局验证(Map文件分析)

编译生成的 .map 文件显示各段地址和大小:

Plaintext

.text 0x08000000 0x4000 // 代码和常量(FLASH)

.data 0x20000000 0x200 // 已初始化变量(RAM)

.bss 0x20000200 0x400 // 未初始化变量(RAM)

.heap 0x20000600 0x1000 // 堆区域

.stack 0x20001600 0x800 // 栈区域

8. 实战注意事项

避免栈溢出:

优化RAM使用:
// 将大型只读数据放入FLASH
const uint8_t large_lookup_table[1024] __attribute__((section(".rodata"))) = { ... };

动态内存替代方案:

9. 链接脚本高级配置

通过自定义段实现精细控制:

ld

/* 将特定函数放入快速执行区域 */

.fast_code : {

*(.fast_code) /* 自定义段 */

} > FLASH_FAST

/* 将关键变量放入备份RAM(掉电保持) */

.backup_ram : {

_backup_start = .;

*(.backup_data) /* 自定义段 */

_backup_end = .;

} > BACKUP_RAM

通过深入理解内存段的划分,开发者可以优化资源使用,避免常见内存错误,并提升系统可靠性。建议结合具体芯片手册和调试工具(如STM32CubeIDE的Memory Analysis)实时验证内存分配。

 

.bss 段详细介绍

(Block Started by Symbol)

在单片机或嵌入式系统中,内存的 .bss 段(Block Started by Symbol)是一个关键内存区域,用于存储未初始化或显式初始化为零的全局变量和静态变量。它的核心目的是优化存储空间,并确保程序的正确初始化。以下是详细解释:

1. .bss 段的核心特性

特性

说明

存储内容

未初始化的全局变量、静态变量,或显式初始化为0的变量

存储介质

RAM(掉电丢失数据)

初始化时机

程序启动时由启动代码(Bootloader)清零

空间优化

不占用FLASH空间(仅记录大小信息,无需存储初始值)

2. .bss 段的作用

示例代码

// 未初始化的全局变量 → 位于.bss段

uint32_t global_uninitialized;

// 显式初始化为0 → 也存放在.bss段

uint32_t global_zero_initialized = 0;

void func() {

static int static_uninitialized; // 未初始化的静态变量 .bss段

}

3. 为什么需要.bss段?

4. .bss 段的工作流程

步骤1:编译阶段

步骤2:链接阶段

步骤3:启动阶段

5. .bss 段与.data段的区别

对比项

.bss

.data

初始化方式

启动时清零

启动时从FLASH复制初始值到RAM

FLASH占用

不存储初始值(仅记录大小)

存储初始值

变量类型

未初始化或初始化为0的变量

已初始化的全局/静态变量

RAM内容初始化

全部清零

由FLASH中的初始值覆盖

6. 实际调试技巧

(1) 查看.bss段大小

.bss 0x20000000 0x400 // 地址和长度(单位:字节)

(2) 验证.bss段是否被清零

(3) 避免.bss段溢出

7. 注意事项

显式初始化为零的变量:
int x = 0; // 位于.bss段(编译器优化)
编译器会将其放入 .bss 段而非 .data 段,以减少FLASH占用。

未初始化的变量不等于随机值:
在嵌入式系统中,启动代码会清零
.bss 段,因此变量的初始值为 0,而非未定义值。

静态局部变量:
void func() {
static int y; // 未初始化的静态局部变量 位于.bss段
}

优化RAM使用:
将初始值为零的大数组放在
.bss 段而非 .data 段,避免占用FLASH空间:
uint8_t large_buffer[1024]; // 正确 位于.bss段
// 而非:

uint8_t large_buffer[1024] = {0}; // 错误 位于.data段(浪费FLASH)

3 全局变量的内存分段规则

变量类型

存储段

存储介质

生命周期

说明

已初始化的全局变量

.data

RAM

程序运行期间

初始值存储在FLASH中,启动时复制到RAM

未初始化的全局变量

.bss

RAM

程序运行期间

启动时由启动代码清零(初始值为0)

初始化为0的全局变量

.bss

RAM

程序运行期间

编译器优化后等同于未初始化变量

const修饰的全局变量

.rodata

FLASH(ROM)

程序生命周期内

只读,不可修改(存放于FLASH中)

静态全局变量

.data.bss

RAM

程序运行期间

静态全局变量遵循与普通全局变量相同的规则

3.1 示例代码与段分配

// 1. 已初始化的全局变量 -> .data段(RAM)

int global_initialized = 42;

// 2. 未初始化的全局变量 -> .bss段(RAM)

int global_uninitialized;

// 3. 显式初始化为0的全局变量 -> .bss段(RAM)

int global_zero_initialized = 0;

// 4. const修饰的全局变量 -> .rodata段(FLASH)

const int global_const = 100;

// 5. 静态全局变量 -> .data或.bss段(RAM)

static int static_global = 5; // .data段(已初始化)

static int static_global_uninit; // .bss段(未初始化)

详细说明

1. 已初始化的全局变量(.data段)

2. 未初始化的全局变量(.bss段)

3. const修饰的全局变量(.rodata段)

4. 静态全局变量

验证方法

1. 查看编译生成的Map文件

2. 调试器观察变量地址

特殊场景与注意事项

1. 初始化为0的变量优化

2. 静态局部变量

3. 多文件中的全局变量

总结

全局变量的存储段完全由初始化状态和修饰符决定:

合理规划全局变量的初始化方式,可显著优化FLASH和RAM的使用效率。

4 内存区域编译器划分

编译器将变量分配到不同内存段,具体取决于变量的类型、作用域和存储类别:

内存段

存储内容

生命周期

示例

.text

可执行代码(机器指令)

程序运行期间

void main() { ... }

.data

已初始化的全局变量、静态变量

程序运行期间

int global = 10;

.bss

未初始化或初始化为0的全局/静态变量

程序运行期间

int global_uninit;

栈 (Stack)

局部变量、函数参数

函数调用期间

int local_var = 5;

堆 (Heap)

动态分配的内存(malloc/new

显式释放前持续存在

int* ptr = malloc(100);

.rodata

只读常量(const修饰的全局变量)

程序运行期间

const int PI = 3.14;

2. 变量分配规则

(1) 全局变量

(2) 静态变量

(3) 局部变量

(4) 常量

3. 内存分配流程

步骤1:符号表生成

步骤2:内存段分配

步骤3:地址计算与对齐

步骤4:生成初始化数据

步骤5:栈与堆管理

4. 优化策略

(1) 寄存器分配

(2) 冗余变量消除

(3) 常量传播

5. 动态内存分配

动态内存(堆)由程序员手动管理,编译器生成调用内存管理函数的代码:

int* arr = (int*)malloc(10 * sizeof(int)); // 编译器生成调用malloc的指令

free(arr);

6. 实战验证方法

(1) 查看汇编代码

(2) 分析Map文件

(3) 调试器查看内存

总结

编译器通过类型、作用域和存储类别确定变量内存段,结合对齐和优化策略高效分配内存。理解这些机制有助于编写内存安全的代

 

.data 段大小的决定因素

示例计算

int a = 1; // 4字节 .data段(RAM)

static double b = 2.0; // 8字节 .data段(RAM)

const char c = 'A'; // 1字节 .rodata段(Flash,不计入.data)

2. 如何控制 .data 段大小?

(1) 减少已初始化的全局/静态变量

(2) 链接脚本(Linker Script)配置

在链接脚本中显式定义 .data 段的地址和空间大小,防止溢出:

MEMORY {

FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K /* Flash配置 */

RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K /* RAM配置 */

}

SECTIONS {

.data : {

_data_start = .;

*(.data*) /* 所有.data段内容 */

_data_end = .;

} > RAM AT > FLASH /* .data段占用的RAM空间根据变量实际大小动态分配 */

}

(3) 结构体对齐优化

3. 验证 .data 段大小

(1) 生成Map文件

在编译选项中添加 -Wl,-Map=output.map,生成的 output.map 文件会显示各段大小:

.data 0x20000000 0x18 /* 0x18 = 24字节 */

global_var 0x20000000 0x4

static_var 0x20000004 0x8

...

(2) 使用 size 命令

查看可执行文件各段大小:

arm-none-eabi-size firmware.elf

输出示例:

text data bss dec hex filename

12345 678 901 13924 3664 firmware.elf

(3) 调试器查看符号地址

使用调试工具(如GDB)检查变量的地址范围:

(gdb) p &global_var # 输出 0x20000000(RAM地址)

(gdb) p &static_var # 输出 0x20000004

4. 解决 .data 段溢出的问题

如果 .data 段超出RAM容量:

5. 实战示例

场景:某STM32工程 .data 段过大,需优化。

步骤1:识别占用大的变量

int buffer[1024] = {0}; // 初始化为0 实际在.bss段(无需优化)

const char* strings[] = {"Hello", "World"}; // 指针数组在.data段,字符串在.rodata段

步骤2:优化代码 将常量指针改为 const,直接指向 .rodata

const char* const strings[] = {"Hello", "World"}; // 指针本身移至.rodata

步骤3:重新编译并检查Map文件

.data 0x20000000 0x8 /* 优化后大小减少 */

 

6 .text 段存储与执行

在嵌入式系统中,.text 段存储编译后的 可执行代码(机器指令),其物理位置位于 ROM非易失性存储器(通常是 Flash) 中。以下是详细说明:

1. .text 段的内容

示例代码

// main函数 → 编译后代码存入.text段

int main() {

while(1) {

// ...

}

}

// 中断服务程序 → 代码存入.text段

void SysTick_Handler(void) {

// ...

}

2. .text 段的存储介质

(1) Flash(非易失性存储器)

(2) 特殊情况:RAM 中执行代码

3. 为什么代码必须存储在 Flash 中?

非易失性需求:
代码需要长期保存,断电后不丢失。

空间容量:
Flash 容量通常远大于 RAM(如 STM32F4 的 Flash 可达 2MB,RAM 仅 192KB)。

安全性:
防止程序运行时意外修改自身代码。

4. .text 段的加载与执行流程

烧录阶段:
编译器将
.text 段代码写入 Flash(通过烧录工具如 ST-Link、J-Link)。

启动阶段:
MCU 上电后,从 Flash 的复位向量(Reset Vector)地址(如
0x08000004)读取指令,开始执行代码。

运行时:
CPU 直接从 Flash 读取指令执行,或通过缓存加速。

5. 验证 .text 段地址的方法

(1) 查看编译生成的 Map 文件

在编译选项中添加 -Wl,-Map=output.map,生成的文件中会显示 .text 段地址:

.text 0x08000000 0x4000 /* 起始地址和大小 */

(2) 调试器查看函数地址

使用 GDB 或 IDE 调试工具(如 Keil、STM32CubeIDE)查看函数地址:

(gdb) p main # 输出类似 0x08000123(位于Flash地址范围内)

(3) 反汇编代码

通过 objdump 生成反汇编文件:

arm-none-eabi-objdump -d firmware.elf > disassembly.txt

查看反汇编结果:

08000123 <main>:

8000123: b480 push {r7}

8000125: af00 add r7, sp, #0

8000127: e7fe b.n 8000127 <main+0x4>

6. 优化 .text 段大小的技巧

编译器优化选项:

避免冗余代码:

手动指定代码段:
高频代码放入
.ram_code 段,减少 Flash 访问次数。

总结

 

  • 代码从 Flash 直接执行,可通过缓存或 RAM 加速关键路径。
  • 通过 Map 文件、调试工具和反汇编验证代码位置,结合编译选项优化空间占用。
  • .text 段存储程序的机器指令,位于 Flash 中,地址范围由芯片决定(如 0x08000000)。
  • 禁用未使用的中断处理函数(Weak 函数需覆盖)。
  • 减少库函数链接(如替换 printf 为轻型实现)。
  • -Os(优化代码大小):
    arm-none-eabi-gcc -Os -o firmware.elf main.c
  • -ffunction-sections & -Wl,--gc-sections
    移除未使用的函数。
  • 目的:提升高频执行代码的性能(如实时信号处理算法)。
  • 实现方式:
  • 手动将代码从 Flash 复制到 RAM。
  • 通过链接脚本将特定函数分配到 RAM 段。
  • 示例(链接脚本):
    .ram_code : {
    *(.ram_code) /* 将.ram_code段分配到RAM */
    } > RAM AT > FLASH /* 初始值在Flash,启动时复制到RAM */
  • 代码注解:
    __attribute__((section(".ram_code"))) void fast_function() {
    // 高频执行代码(从RAM运行)
    }
  • 特性:
  • 只读(Read-Only):运行时不可修改。
  • 掉电数据不丢失。
  • 读取速度较慢(相比 RAM),但现代 MCU 通常通过指令缓存(如 ARM Cortex-M7 的 I-Cache)加速。
  • 地址范围:
  • 典型 Cortex-M 设备的 Flash 起始地址为 0x08000000
  • 示例:STM32F4 的 Flash 地址范围为 0x08000000 ~ 0x081FFFFF(2MB)。
  • 机器指令:函数、中断服务程序(ISR)、启动代码等。
  • 只读数据(部分编译器可能纳入 .text.rodata):
  • 中断向量表(如 ARM Cortex-M 的向量表)。
  • 编译器生成的常量跳转表(如 switch-case 语句的跳转表)。
  • 优化核心:减少初始化数据、利用.bss.rodata段、调整内存对齐。
  • 通过Map文件和调试工具验证优化效果,确保RAM和Flash资源合理分配。X
  • .data段大小由已初始化的全局/静态变量决定,需在代码和链接脚本中协同优化。
  • strings 的指针地址在 .data 段,字符串内容在 .rodata 段。
  • 优化代码:减少全局/静态变量数量或大小。
  • 调整内存分配:在链接脚本中扩展RAM空间或优化其他段(如.bss)。
  • 使用内存覆盖:将部分数据存储在Flash中,运行时按需加载(牺牲速度换空间)。
  • data 列表示 .data 段大小(单位字节)。
  • 使用 #pragma pack 减少结构体内存空洞:
    #pragma pack(1) // 1字节对齐
    struct SensorData {
    uint8_t id; // 1字节
    uint32_t value; // 4字节
    }; // 总大小=5字节(默认对齐时为8字节)
    #pragma pack() // 恢复默认对齐
  • 关键点:.data 段的RAM占用由变量实际大小决定,Flash镜像大小与之相同。
  • 优化策略:
  • 将变量改为 未初始化(移至 .bss 段):
    int a; // 从 .data 改为 .bss(初始值0,不占用Flash)
  • 使用 const 修饰符将常量放入 .rodata 段:
    const int table[100] = { ... }; // 移入Flash的.rodata段
  • RAM占用:a + b = 4 + 8 = 12 字节(.data段)。
  • Flash占用:.data段的初始值镜像占用同样 12 字节。
  • 已初始化的全局变量:例如 int global = 10;
  • 已初始化的静态变量:例如 static float static_var = 3.14;
  • 初始值存储开销:每个变量的初始值会占用 Flash空间(启动时需复制到RAM)。
  • 使用GDB或IDE调试工具,直接查看变量地址:
    (gdb) p &global_var
    # 输出类似 0x20000000(RAM地址)
    (gdb) p &const_var # 输出类似 0x08001000(Flash地址)
  • 链接生成的Map文件(-Wl,-Map=output.map)显示各段地址和变量布局:
    .text 0x08000000 0x200
    .data 0x20000000 0x10
    .bss 0x20000010 0x8
  • GCC生成汇编代码:
    gcc -S main.c -o main.s
  • 观察变量地址分配:
    .data
    global_var: .word 10 # .data段
    .bss
    static_var: .space 4 # .bss段
    .text
    main:
    pushl %ebp
    movl %esp, %ebp
    subl $16, %esp # 栈分配16字节(含局部变量)
  • 直接替换常量值到使用位置:
    const int N = 100;
    int arr[N]; // 替换为 int arr[100];
  • 删除未使用的变量:
    int unused_var = 10; // 被优化删除
  • 高频使用的局部变量优先存入寄存器(减少内存访问)。
    register int i; // 建议编译器使用寄存器(实际由编译器决定)
  • 栈分配:编译器生成代码,通过移动栈指针(SP)分配局部变量。
    x86示例:分配8字节栈空间
    sub esp, 8
  • 堆分配:向操作系统请求内存(如malloc调用brkmmap)。
  • 已初始化的全局/静态变量,编译器将初始值写入Flash的.data镜像区。
    int initialized = 0x1234; // 初始值0x1234存储在Flash,启动时复制到RAM
  • 填充 (Padding):插入空字节避免访问错误。
    struct Example {
    char a; // 1字节
    // 填充3字节

    int b; // 4字节(地址为4的倍数)
    }; // 总大小 = 8字节
  • char:1字节对齐
  • int:4字节对齐
  • double:8字节对齐
  • 计算变量在段内的偏移地址,满足内存对齐要求。
  • 对齐规则:
  • 根据变量属性确定内存段:
    int global = 10; // .data段
    static float static_var; // .bss段
    void func() {
    int local; // 栈
    }
  • 编译器解析代码,记录变量类型、作用域和存储类别。
  • 示例:static int x; 符号表标记为静态变量,存入.bss段。
  • 字符串字面量 .rodata段(如 char* str = "Hello";
  • const全局变量 .rodata段(Flash,只读)
  • 普通局部变量 栈内存(函数调用时自动分配)
  • 寄存器变量 可能被优化到CPU寄存器(register int i;
  • 静态全局变量 类似全局变量(.data.bss
  • 静态局部变量 .data段(已初始化)或.bss段(未初始化)
  • 已初始化 .data段(RAM,初始值存储在Flash)
  • 未初始化 .bss段(RAM,启动时清零)
  • .data段:初始化为非零值的全局变量。
  • .bss段:未初始化或初始化为零的全局变量。
  • .rodata段:const 修饰的全局常量。
  • 跨文件的全局变量需使用 extern 声明:
    // file1.c
    int global_var = 42; // 定义在.data段
    // file2.c

    extern int global_var; // 声明使用其他文件中定义的全局变量
  • 静态局部变量与静态全局变量的分配规则一致:
    void func() {
    static int static_local = 10; // 已初始化 -> .data段
    static int static_local_uninit; // 未初始化 -> .bss段
    }
  • 显式初始化为 0 的变量会被编译器优化到 .bss 段,但需验证编译器行为:
    int x = 0; // 可能被优化到.bss段
  • .data 段变量地址在RAM范围内(如 0x2000xxxx)。
  • .rodata 段变量地址在FLASH范围内(如 0x0800xxxx)。
  • 通过调试工具(如Keil、IAR或GDB)查看变量地址:
  • 在编译选项中添加 -Wl,-Map=output.map,生成的 output.map 文件会显示全局变量的段地址:
    .data 0x20000000 0x8 // 已初始化全局变量
    global_initialized 0x20000000 0x4
    static_global 0x20000004 0x4
    .bss 0x20000008 0x8 // 未初始化全局变量
    global_uninitialized 0x20000008 0x4
    static_global_uninit 0x2000000c 0x4
    .rodata 0x08001000 0x4 // 只读常量
    global_const 0x08001000 0x4
  • 作用域:仅在定义它的文件内可见(与普通全局变量的全局作用域不同)。
  • 存储规则:与普通全局变量完全相同,根据初始化状态分配在 .data.bss 段。
  • 特性:只读,存储在FLASH中,无法修改。
  • 应用场景:常量表、配置文件等无需修改的数据。
  • 访问限制:
    const int READ_ONLY_DATA = 0x1234;
    // READ_ONLY_DATA = 0x5678; // 编译报错:尝试修改只读数据
  • 定义:未显式初始化,或显式初始化为 0
  • 存储方式:
  • 不占用FLASH空间,仅在RAM中分配区域。
  • 启动代码将 .bss 段对应的RAM区域清零。
  • 编译器优化:
  • 初始化为 0 的变量会被编译器优化到 .bss 段,减少FLASH占用。
  • 示例:int x = 0; 实际存储在 .bss 段而非 .data 段。
  • 定义:在声明时被赋予非零初始值。
  • 存储方式:
  • 初始值存储在FLASH的 .data 镜像区。
  • 程序启动时,启动代码将FLASH中的初始值复制到RAM的 .data 段。
  • 链接脚本配置:
    .data : {
    _data_start = .; /* RAM中的.data起始地址 */
    *(.data*) /* 所有已初始化的全局变量 */
    _data_end = .;
    } > RAM AT > FLASH /* 物理存储在FLASH,运行时在RAM */
  • 禁止未初始化变量:通过编译器选项(如 -Werror=uninitialized)强制变量必须显式初始化。
  • 静态分析工具:使用工具检查未初始化变量的使用。
  • 初始化为 NULL(即 0)的指针位于 .bss 段:
    int* ptr = NULL; // 位于.bss段
  • 可以,通过编译器属性强制指定变量到 .bss 段:
    uint32_t my_var __attribute__((section(".bss")));
  • 在链接脚本中检查RAM剩余空间是否足够容纳 .bss 段:
    Memory region Used Size Free Size
    RAM 0x400 0x1000 // 已用1KB,剩余4KB
  • 在调试器中,观察 .bss 段变量的初始值是否为零:
    extern uint32_t global_uninitialized;
    // 调试时检查 global_uninitialized == 0
  • 通过编译生成的 Map文件(如 output.map)查看 .bss 段地址和大小:
  • 单片机上电后,启动代码(Startup Code)清零 .bss 段。
    示例汇编代码(ARM Cortex-M):

    ldr r0, =_bss_start /* 加载.bss段起始地址 */
    ldr r1, =_bss_end /* 加载.bss段结束地址 */
    mov r2, #0 /* 清零值 */
    clear_bss_loop:
    cmp r0, r1 /* 检查是否到达结束地址 */
    strlt r2, [r0], #4 /* 清零内存并递增指针 */
    blt clear_bss_loop /* 循环直到完成 */
  • 链接脚本(Linker Script)定义 .bss 段在RAM中的位置。
    示例链接脚本片段:
    ld
    .bss : {
    _bss_start = .; /* 记录.bss段起始地址 */
    *(.bss*) /* 所有.bss段内容 */
    _bss_end = .; /* 记录.bss段结束地址 */
    } > RAM
  • 编译器识别未初始化的全局/静态变量,标记为 .bss 段。
  • 在生成的符号表中记录 .bss 段的起始地址和大小。
  • 减少二进制文件体积:
    如果未初始化的变量放在
    .data 段,需要在FLASH中存储零值,浪费空间。
    .bss 段只需记录长度信息,无需存储实际数据。
  • 提高启动效率:
    启动时只需将
    .bss 段对应的RAM区域清零,而不是逐个复制初始值(如 .data 段)。
  • 节省FLASH空间:
    未初始化的变量不需要在FLASH中存储初始值,仅在运行时分配RAM空间并初始化为零。
  • 统一初始化逻辑:
    所有未初始化或零初始化的变量统一管理,避免分散处理。
  • 使用内存池或对象池替代 malloc,减少碎片。
  • 例:静态预分配缓冲区管理:
    c

    #define POOL_SIZE 10
    static uint8_t memory_pool[POOL_SIZE][256]; // 预分配内存池
    static bool pool_used[POOL_SIZE] = {0};
    void* my_alloc() {
    for (int i = 0; i < POOL_SIZE; i++) {
    if (!pool_used[i]) {
    pool_used[i] =
    true;
    return memory_pool[i];
    }
    }

    return NULL; // 分配失败
    }
  • 使用静态分析工具(如 -Wstack-usage)监控栈深度。
  • 避免在函数内定义大型局部数组,改用堆或全局数组。
  • 适用平台:ARM Cortex-M4/M7等高阶MCU。
  • 存储内容:高频访问数据(如DMA缓冲区或实时任务数据)。
  • 优势:独立总线访问,可与CPU指令执行并行。
  • 存储内容:局部变量、函数调用时的返回地址、寄存器保存和参数传递。
  • 存储介质:RAM,自动分配和释放。
  • 生命周期:函数调用期间存在。
  • 风险:栈溢出(如递归过深或大型局部数组)。
  • 存储内容:动态分配的内存(如 malloc/free 管理)。
  • 存储介质:RAM,手动分配和释放。
  • 生命周期:从 mallocfree 期间有效。
  • 风险:内存碎片、泄漏、溢出。
  • 存储内容:未初始化或显式初始化为0的全局变量、静态变量。
  • 存储介质:RAM,启动时由启动代码清零。
  • 生命周期:程序运行期间持续存在。
  • 优势:节省FLASH空间,无需存储初始值。
  • 存储内容:已初始化的全局变量、静态变量。
  • 存储介质:RAM(运行时),但其初始值存储在FLASH中,启动时由启动代码复制到RAM。
  • 生命周期:程序运行期间持续存在。
  • 只读属性:尝试修改 .text 段内容会导致硬件错误(如ARM的 HardFault)。
  • 优化技巧:频繁调用的函数可使用 __attribute__((section(".fast_code"))) 分配到快速FLASH区域(如果支持)。
  • 存储内容:编译后的机器指令(代码)和只读常量(如 const 变量)。
  • 存储介质:FLASH(ROM),掉电不丢失。
  • 生命周期:程序生命周期内始终存在,不可修改。

http://www.kler.cn/a/612191.html

相关文章:

  • 《Python实战进阶》No34:卷积神经网络(CNN)图像分类实战
  • 【C++】httplib:轻量级的 HTTP 服务器和客户端
  • vue 双向绑定的原理是什么
  • Linux Namespace(网络命名空间)系列三 --- 使用 Open vSwitch 和 VLAN 标签实现网络隔离
  • Redis 性能数据解读与问题排查优化版
  • 基于Flask的防火墙知识库Web应用技术解析
  • python爬虫PyQt简介
  • 【QT5 多线程示例】信号量
  • C++学习笔记(二十九)——list
  • 【Linux网络-poll与epoll】epollserver设计(两个版本 Reactor)+epoll理论补充(LT ET)
  • vue ts+Windi CSS
  • CTFshow【命令执行】web29-web40 做题笔记
  • 未来工程项目管理新走向:云原生软件赋能绿色可持续建设
  • Kafka 面试备战指南
  • eureka与ribbon混合使用
  • Linux设置SSH免密码密钥登录
  • Netty和Project Reactor如何共同处理大数据流?
  • 无人机抗风测试技术要点概述!
  • failed to load steamui.dll”错误:Steam用户的高频崩溃问题解析
  • LLaMA-Factory使用实战