[MCU开发工具资源区] 使用Keil的MicroLIB时自动设置堆大小

[复制链接]
1746|9
 楼主| pl202 发表于 2024-5-30 17:04 | 显示全部楼层 |阅读模式
Keil编译项目,如果使用微库MicroLIB,就可以使用malloc。微库内部位置一个堆管理模块。
芯片的RAM大小是固定了的,前面分为全局变量,后面分给堆和栈,这是一般开发方式。
但是我们在开发项目的过程中,市场遇到各种各样问题,栈穿透到堆里面,或者堆不够大,相当烦人!
有时候就在想,何不让全局变量以外的所有RAM给堆栈共用?
因为堆从低到高分配,而栈从高到低分配,理论上是可行的!

但是堆的分配由__heap_base和__heap_limit两个标签决定,不是变量又不能改!

因为我们使用很多种芯片,每一种芯片的RAM大小都有可能不同。
而SmartOS追求跨平台,不想为不同芯片做太多设置。
之前我们已经实现了通过修改MSP把栈顶移到RAM最高处,这样子栈可以得到最大利用!
但是堆还是不好搞!

今晚再次遇到堆不够用的情况,__heap_limit如果分配过大,在小容量芯片就会出错。
忍无可忍,决定分析一下微库是怎么管理堆的。

首先打开项目编译后生成的链接地址映射文件Linker Address Map,我们这里是SmartOSF0_Debug.map
找到符号表段Global Symbols
    __heap_base                              0x200005a0   Data           0  startup_stm32f0xx.o(HEAP)
    __heap_limit                             0x200005a0   Data           0  startup_stm32f0xx.o(HEAP)
    __initial_sp                             0x200005c0   Data           0  startup_stm32f0xx.o(STACK)
从这里可以看出,堆栈已经分配好了。


堆分配使用的是malloc函数,上图找到它位于Keil库文件mc_p.l中
我的目录是D:\Keil\ARM\ARMCC\lib\armlib

轮到法宝IDA上阵,选择malloc.o,太简单了,只有malloc/free两个函数
汇编图形界面如下:


手头的IDA没有ARM插件,否则一个F5就可以得到malloc的C源代码。
好好工作赚钱卖ARM插件吧。。。

没有工具辅助,那就自己来写吧!


上面是手工写的C代码,被注释的是最原始的汇编写法,然后逐步精简优化。
大概摸清楚了malloc的机制,关键点在于初始化那里,用到了__heap_limit
而__heap_limit作为常量被编译到Flash里面去了,内存里面无法动态修改。
实在没办法,只好字节写代码来接替它初始化整个堆了。

  1. void free(void* p)
  2. {
  3.     if(!p) return 0;
  4.    
  5.     r3 = __microlib_freelist;
  6.     void* r2 = 0;
  7.     p -= 4;
  8.     void* r1 = *r3; // r1 = r3->node
  9.     while(r1)
  10.     {
  11.         if(r1 > p) break;
  12.         r2 = r1;
  13.         r1 = *(r1 + 4); // r1 = r1->next
  14.     }
  15.     if(!r2)
  16.         r3->node = p;
  17.     else
  18.     {
  19.         r3 = *r2;
  20.         r4 = p - r2;
  21.         if(r4 != r3)
  22.             *(r2 + 4) = p;
  23.         else
  24.         {
  25.             p = *p;
  26.             p += r3;
  27.             
  28.         }
  29.     }
  30. }

  31. typedef struct
  32. {
  33.     uint size;
  34.     void* next;
  35. } Node;

  36. __microlib_freelist:
  37.     Node* _freelist;

  38. __microlib_freelist_initialised:
  39.     int _freelist_initialised = 0;

  40. void* malloc(int size)
  41. {
  42.     /*r0 += 0x0b;
  43.     r0 >>= 3;
  44.     r0 <<= 3;*/
  45.     r1 = (size + 11) & 0xFFFFFFF8;
  46.     r7 = __microlib_freelist_initialised;
  47.     r6 = 0;

  48.     /*r2 = __microlib_freelist;
  49.     if(!*r2 && !*r7)*/
  50.     if(!_freelist && !_freelist_initialised)
  51.     {
  52.         /*r2 = __microlib_freelist;
  53.         r0 = __heap_base + 4;
  54.         *r2 = r0;
  55.         r2 = __heap_limit;
  56.         r2 -= r0;
  57.         r2 &= 0xFFFFFFF0;
  58.         *r0 = r2;
  59.         *(r0 + 4) = 0;
  60.         *r7 = 1;*/

  61.         _freelist = (Node*)(__heap_base + 4);
  62.         _freelist->size = (__heap_limit - __heap_base - 4) & 0xFFFFFFF8;
  63.         _freelist->next = 0;
  64.         _freelist_initialised = 1;
  65.     }

  66.     //r2 = __microlib_freelist;
  67.     r0 = _freelist;
  68.     while(true)
  69.     {
  70.         /*r0 = *r2;
  71.         if(!r0)
  72.         {
  73.             r0 = r6;
  74.             break;
  75.         }*/
  76.         //r0 = *r2;
  77.         if(!r0) return 0;

  78.         //r3 = *r0;
  79.         r3 = r0->size;
  80.         if(r3 <= r1) break;
  81.         
  82.         //r2 = r0 + 4;
  83.         //r2 = r0->next;
  84.         r0 = *(r0->next);
  85.     }
  86.     if(r3 <= r1)
  87.     {
  88.         //r3 = *(r0 + 4);
  89.         /*r3 = _freelist->next;
  90.         *r2 = r3;*/
  91.         _freelist->size = r1;
  92.         _freelist = _freelist->next;
  93.         return &;
  94.     }
  95.     else
  96.     {
  97.         /*r4 = r3 - r1;
  98.         r3 = r0 + r1;
  99.         r5 = *(r0 + 4);
  100.         *r3 = r4;
  101.         r3 +=4 ;
  102.         *(r3 + 4) = r5;
  103.         r3 +=4 ;
  104.         r3 -= 8;// 减8为了回到r3开始*/
  105.         
  106.         Node* lst = (void*)_freelist + r1;
  107.         lst->size = _freelist->size - r1;
  108.         lst->next = _freelist->next;
  109.         
  110.         _freelist->size = r1;
  111.         _freelist = lst;
  112.         
  113.         return &lst->next;
  114.     }
  115.    
  116.     return r0;
  117. }


alxd 发表于 2024-6-6 17:18 | 显示全部楼层
自动设置可不会啊,一般不都是手动调节的吗
B1lanche 发表于 2024-6-6 18:24 | 显示全部楼层
这种方式好像很不错,可以自动设置堆的大小,很方便啊
weifeng90 发表于 2024-6-6 19:02 来自手机 | 显示全部楼层
KEIL还支持安装插件吗?
Candic12e 发表于 2024-6-6 19:33 | 显示全部楼层
一般堆栈的设置,配置一次好像就可以了吧
Belle1257 发表于 2024-6-7 22:04 | 显示全部楼层
正常来说,分配的就可以用吧,实在不够的话,手动改就好了
AdaMaYun 发表于 2024-6-14 08:19 | 显示全部楼层
使用微库内部位置一个堆管理模块简单
OKAKAKO 发表于 2024-6-21 21:19 | 显示全部楼层
芯片的RAM大小是固定了的,前面分为全局变量,后面分给堆和栈
中国龙芯CDX 发表于 2024-6-26 16:36 | 显示全部楼层
SmartOS追求跨平台,不想为不同芯片做太多设置
jf101 发表于 2024-6-27 16:56 | 显示全部楼层
使用Keil的MicroLIB时自动设置堆大小非常实用
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

3028

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部