基于STM32F103RCx的RAM空间分布探讨

确认几个量 及 概念

        此验证为了方便,处于裸机,并无上RTOSFree操作系统

Stack_Size        EQU     0x400        : 栈区大小

Heap_Size      EQU     0x200        : 堆区大小

__heap_base        : 堆起始地址

__heap_limit        : 堆结束地址

__initial_sp        : 栈顶地址

目录

先上结论:

KEIL查看:

测试开始:

一、无HEAP区

二、加入HEAP堆区

三、HEAP可使用大小

四、栈区可用大小


先上结论:

.text        代码段

.data        初始化的全局变量 与 静态变量

__stdout        占用0x04字节

.bss        未初始化的全局变量 与 静态变量

0x08字节地址对齐

HEAP        堆区

        __heap_base        0x08个字节不可用

        __heap_base + 0x08 + 0x04 用户实际可使用的最开始地址,实际使用空间后续会验证

STACK        栈区

        实际使用情况下,__initial_sp - 0x08 开始的0x08字节无法使用

__initial_sp 栈顶

注: 1.__initial_sp 栈顶不会是RAM大小,是在编译后生成的

        2.如果整个工程没有使用过malloc,则在RAM中不会有HEAP区域

        3.malloc超了大小,会直接挂掉程序;局部变量超了大小不一定会挂程序,如果有堆区就会使用到堆区,没有堆区则会直接使用未初始化区,但最好不要超了

KEIL查看:

Code -- 代码大小

RO -- 常量占用空间(FLASH)

RW -- 程序初始化的变量(RAM,0x08字节对齐)

ZI --- 未初始化的static、全局变量、以及堆栈(RAM)

.bin大小(也是升级所需的文件大小) = Code + RO + RW + ZI

__initial_sp (使用RAM的大小) = RW + ZI

测试开始:

一、无HEAP区

首先搞个最开始的空文件

#define UART_DATABUFF_MAXLEN    0x96    // 150
void user_test(void)
{
  log_debug("0x%08X", uartRecBuff);
  return;
}

Program Size: Code=7484 RO-data=360 RW-data=24 ZI-data=1384  

Code 和 RO暂且不论

此时__initial_sp = RW + ZI = 0x580

打开.map文件,找到相应的内存放置

    .data                                    0x20000000   Section       12  stm32f1xx_hal.o(.data)
    .data                                    0x2000000c   Section        4  system_stm32f1xx.o(.data)
    .data                                    0x20000010   Section        4  user_uart.o(.data)
    .data                                    0x20000014   Section        4  stdout.o(.data)
    .bss                                     0x20000018   Section      204  usart.o(.bss)
    .bss                                     0x200000e4   Section      150  user_uart.o(.bss)
    uartRecBuff                              0x200000e4   Data         150  user_uart.o(.bss)
    STACK                                    0x20000180   Section     1024  startup_stm32f103xe.o(STACK)

    uwTickFreq                               0x20000000   Data           1  stm32f1xx_hal.o(.data)
    uwTickPrio                               0x20000004   Data           4  stm32f1xx_hal.o(.data)
    uwTick                                   0x20000008   Data           4  stm32f1xx_hal.o(.data)
    SystemCoreClock                          0x2000000c   Data           4  system_stm32f1xx.o(.data)
    pDegUart                                 0x20000010   Data           4  user_uart.o(.data)
    __stdout                                 0x20000014   Data           4  stdout.o(.data)
    huart1                                   0x20000018   Data          68  usart.o(.bss)
    hdma_usart1_rx                           0x2000005c   Data          68  usart.o(.bss)
    hdma_usart1_tx                           0x200000a0   Data          68  usart.o(.bss)
    __initial_sp                             0x20000590   Data           0  startup_stm32f103xe.o(STACK)

这里会发现STACK地址减去uartRecBuff为0x9C,比设置的0x96多出了0x06个字节,这0x06个字节是进行对齐使用的,使得STACK能够被0x08整除

堆栈前需要0x08字节对齐的测试如下:

代码段

static uint8_t buff[0x16];
void user_test(void)
{
  log_debug("0x%08X", buff);
  return;
}

map段

    uartRecBuff                              0x200000e4   Data         150  user_uart.o(.bss)
    buff                                     0x2000017a   Data          22  user_uart.o(.bss)
    STACK                                    0x20000190   Section     1024  startup_stm32f103xe.o(STACK)

即0x190 - 0x17a = 0x16

----------------------------------------------------------------------------
代码段

static uint8_t buff[0x17];
void user_test(void)
{
  log_debug("0x%08X", buff);
  return;
}

map段
    uartRecBuff                              0x200000e4   Data         150  user_uart.o(.bss)
    buff                                     0x2000017a   Data          23  user_uart.o(.bss)
    STACK                                    0x20000198   Section     1024  startup_stm32f103xe.o(STACK)

即0x198 - 0x17a = 0x1E, 0x1E - 0x17 = 0x07,被自动补齐了剩下的7个字节

第一步验证出来的RAM区域使用情况就是:

.text        代码段

.data        初始化的全局变量 与 静态变量

__stdout        占用4字节

.bss        未初始化的全局变量 与 静态变量

0x08字节对齐

STACK        栈区

__initial_sp        栈顶

二、加入HEAP堆区

static uint8_t buff[0x10];
void user_test(void)
{
  uint8_t *ptmp1 = (uint8_t *)malloc(0x50);
  log_debug("0x%08X", ptmp1);
  uint8_t *ptmp2 = (uint8_t *)malloc(0x50);
  log_debug("0x%08X", ptmp2);
  uint8_t *ptmp3 = (uint8_t *)malloc(0x50);
  log_debug("0x%08X", ptmp3);
  log_debug("0x%08X", uartRecBuff);
  log_debug("0x%08X", buff);
  return;
}

-> Debug : 0x200001A0

-> Debug : 0x200001F8

-> Debug : 0x20000250

-> Debug : 0x200000EC

-> Debug : 0x20000182

Program Size: Code=7652 RO-data=360 RW-data=32 ZI-data=1912  

    .data                                    0x20000000   Section       12  stm32f1xx_hal.o(.data)
    .data                                    0x2000000c   Section        4  system_stm32f1xx.o(.data)
    .data                                    0x20000010   Section        4  user_uart.o(.data)
    .data                                    0x20000014   Section        4  stdout.o(.data)
    .data                                    0x20000018   Section        4  mvars.o(.data)
    .data                                    0x2000001c   Section        4  mvars.o(.data)
    .bss                                     0x20000020   Section      204  usart.o(.bss)
    .bss                                     0x200000ec   Section      166  user_uart.o(.bss)
    uartRecBuff                              0x200000ec   Data         150  user_uart.o(.bss)
    buff                                     0x20000182   Data          16  user_uart.o(.bss)
    HEAP                                     0x20000198   Section      512  startup_stm32f103xe.o(HEAP)
    STACK                                    0x20000398   Section     1024  startup_stm32f103xe.o(STACK)

    uwTickFreq                               0x20000000   Data           1  stm32f1xx_hal.o(.data)
    uwTickPrio                               0x20000004   Data           4  stm32f1xx_hal.o(.data)
    uwTick                                   0x20000008   Data           4  stm32f1xx_hal.o(.data)
    SystemCoreClock                          0x2000000c   Data           4  system_stm32f1xx.o(.data)
    pDegUart                                 0x20000010   Data           4  user_uart.o(.data)
    __stdout                                 0x20000014   Data           4  stdout.o(.data)
    __microlib_freelist                      0x20000018   Data           4  mvars.o(.data)
    __microlib_freelist_initialised          0x2000001c   Data           4  mvars.o(.data)
    huart1                                   0x20000020   Data          68  usart.o(.bss)
    hdma_usart1_rx                           0x20000064   Data          68  usart.o(.bss)
    hdma_usart1_tx                           0x200000a8   Data          68  usart.o(.bss)
    __heap_base                              0x20000198   Data           0  startup_stm32f103xe.o(HEAP)
    __heap_limit                             0x20000398   Data           0  startup_stm32f103xe.o(HEAP)
    __initial_sp                             0x20000798   Data           0  startup_stm32f103xe.o(STACK)

堆栈的空间是连在一起的,即 __initial_sp - Stack_Size = __heap_limit

__heap_limit - Heap_Size = __heap_base

至此可以添加完善

.test        代码段

.data        初始化的全局变量 与 静态变量

__stdout        占用4字节

.bss        未初始化的全局变量 与 静态变量

0x08字节对齐

HEAP        堆区

STACK        栈区

__initial_sp        栈顶

三、HEAP可使用大小

问:HEAP的大小是0x200就能够malloc0x200的大小吗?

答案是否定的,如上数malloc的三个0x50空间所示

ptmp3 - ptmp2 = ptmp2 - ptmp1 = 0x08

这里先直接上结论malloc之后的空间整体大小是要8的倍数,整体的计算方式:

act_size = (size % 0x08 > 0x04) ? (size / 0x08 + 0x01) * 0x08 : (size / 0x08 + 0x02) * 0x08

其中:act_size 为 实际占用大小;size 为 申请大小

且__heap_base 后的 0x08 字节一定是不可用的

如上所示,__heap_base = 0x198,而ptmp1 = 0x1A0

0x08字节对齐与0x04字节延展验证程序和代码如下:

  uint8_t *ptmp1 = (uint8_t *)malloc(0x14);
  log_debug("0x%08X", ptmp1);
  uint8_t *ptmp2 = (uint8_t *)malloc(0x15);
  log_debug("0x%08X", ptmp2);
  uint8_t *ptmp3 = (uint8_t *)malloc(0x18);
  log_debug("0x%08X", ptmp3);

-> Debug : 0x20000190

-> Debug : 0x200001A8

-> Debug : 0x200001C8

ptmp1 - __heap_base = 0x08;

ptmp2 - ptmp1 - 0x14 = 0x04;

所以,假设用户malloc的量是n个,能够使用的最大malloc的大小是Heap_Size - 0x08 - 0x04 * n

能够使用的最小值是Heap_Size - 0x08 * (n + 0x01)

至于开头的0x08字节,和指针前0x04字节的数据就不进行深究,有兴趣的小伙伴可以打印出来

四、栈区可用大小

接下来再看看栈区的情况,在一个函数内只使用一个局部数据的时候

只是很可惜,栈区是系统自动释放的,在map里面是搜不到相关的信息,只能打印出来

__initial_sp                             0x20000788   Data           0  startup_stm32f103xe.o(STACK)

代码段1
  uint8_t stabuf1[0x50];
  log_debug("0x%08X", stabuf1);

打印段1
-> Debug : 0x20000730

即:0x788 - 0x730 - 0x50 = 0x08

代码段2
  uint8_t stabuf1[0x51];
  log_debug("0x%08X", stabuf1);

打印段2
-> Debug : 0x20000730

即:0x788 - 0x730 - 0x51 = 0x07

代码段3
  uint8_t stabuf1[0x4F];
  log_debug("0x%08X", stabuf1);

打印段3
-> Debug : 0x20000730

即:0x788 - 0x730 - 0x4F = 0x09

----------------------------------------
4:
  uint8_t stabuf1[0x4C];
  log_debug("0x%08X", stabuf1);

-> Debug : 0x20000738

0x788 - 0x738 - 0x4C = 0x04

5:
  uint8_t stabuf1[0x4D];
  log_debug("0x%08X", stabuf1);

-> Debug : 0x20000730

对照1~5可得,同堆区HEAP,当申请的空间加上0x04个字节后被0x08整除不了的时候,需要自动补齐,但由上式代码段可得知,栈顶往下,并没有类似堆区的起始0x08个字节不可用区域

接下来验证当多个局部数组的时候会出现怎样的情况

  uint8_t stabuf3[0x50];
  log_debug("0x%08X", stabuf3);
  uint8_t stabuf2[0x50];
  log_debug("0x%08X", stabuf2);
  uint8_t stabuf1[0x50];
  log_debug("0x%08X", stabuf1);

-> Debug : 0x20000690

-> Debug : 0x200006E0

-> Debug : 0x20000730

这里就会出现0x730 - 0x6E0 = 0x50刚刚好,不会出现如同堆区需要而外的0x04字节

但是!但是!但是!

  uint8_t stabuf4[0x50];
  log_debug("0x%08X", stabuf4);
  uint8_t stabuf3[0x4F];
  log_debug("0x%08X", stabuf3);
  uint8_t stabuf2[0x51];
  log_debug("0x%08X", stabuf2);
  uint8_t stabuf1[0x50];
  log_debug("0x%08X", stabuf1);


-> Debug : 0x20000694

-> Debug : 0x200006E4

-> Debug : 0x20000640

-> Debug : 0x20000734

当全部申请为数组时,这里会发现数组的大小时需要被0x04整除的,而不是0x08

  uint8_t stabuf4;
  log_debug("0x%08X", &stabuf4);
  uint8_t stabuf3[0x4F];
  log_debug("0x%08X", stabuf3);
  uint16_t stabuf2;
  log_debug("0x%08X", &stabuf2);
  uint32_t stabuf5;
  log_debug("0x%08X", &stabuf5);
  uint8_t stabuf1[0x50];
  log_debug("0x%08X", stabuf1);


-> Debug : 0x20000778

-> Debug : 0x200006D8

-> Debug : 0x2000077C

-> Debug : 0x20000780

-> Debug : 0x20000728

这里只打印出来数据类型的分布,看看就好,怎么算法怎么个规律并不用去关心,此时要关心的问题就是,能够使用的STACK堆区大小到底是有多少呢?

以实际情况为例,由于不同堆区的每申请一个都得占用额外的0x04字节,及用以下即可:

注:__heap_limit                             0x20000388

  uint32_t buff;  // uint8_t 和 uint16_t 同样的结果, 但uint64_t 结果不同 
  log_debug("0x%08X", &buff);
  uint8_t stabuf1[0x3F8];
  log_debug("0x%08X", stabuf1);


-> Debug : 0x20000780

-> Debug : 0x20000388

即实际可用栈区大小 act_size = 0x388 + 0x04 = Stack_Size - 0x04

补充结束后就是最开始的先上结论......

os :闭环,最后的最后又会到了开头

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值