[技术问答]

使用Keil的MicroLIB时自动设置堆大小

[复制链接]
1231|17
手机看帖
扫描二维码
随时随地手机跟帖
abotomson|  楼主 | 2024-4-29 21:03 | 显示全部楼层 |阅读模式
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里面去了,内存里面无法动态修改。
实在没办法,只好字节写代码来接替它初始化整个堆了。
我写的初始化代码如下:


附上malloc/free代码,不完整,只能大概了解它的机制:
void free(void* p)
{
    if(!p) return 0;

    r3 = __microlib_freelist;
    void* r2 = 0;
    p -= 4;
    void* r1 = *r3; // r1 = r3->node
    while(r1)
    {
        if(r1 > p) break;
        r2 = r1;
        r1 = *(r1 + 4); // r1 = r1->next
    }
    if(!r2)
        r3->node = p;
    else
    {
        r3 = *r2;
        r4 = p - r2;
        if(r4 != r3)
            *(r2 + 4) = p;
        else
        {
            p = *p;
            p += r3;

        }
    }
}

typedef struct
{
    uint size;
    void* next;
} Node;

__microlib_freelist:
    Node* _freelist;

__microlib_freelist_initialised:
    int _freelist_initialised = 0;

void* malloc(int size)
{
    /*r0 += 0x0b;
    r0 >>= 3;
    r0 <<= 3;*/
    r1 = (size + 11) & 0xFFFFFFF8;
    r7 = __microlib_freelist_initialised;
    r6 = 0;

    /*r2 = __microlib_freelist;
    if(!*r2 && !*r7)*/
    if(!_freelist && !_freelist_initialised)
    {
        /*r2 = __microlib_freelist;
        r0 = __heap_base + 4;
        *r2 = r0;
        r2 = __heap_limit;
        r2 -= r0;
        r2 &= 0xFFFFFFF0;
        *r0 = r2;
        *(r0 + 4) = 0;
        *r7 = 1;*/

        _freelist = (Node*)(__heap_base + 4);
        _freelist->size = (__heap_limit - __heap_base - 4) & 0xFFFFFFF8;
        _freelist->next = 0;
        _freelist_initialised = 1;
    }

    //r2 = __microlib_freelist;
    r0 = _freelist;
    while(true)
    {
        /*r0 = *r2;
        if(!r0)
        {
            r0 = r6;
            break;
        }*/
        //r0 = *r2;
        if(!r0) return 0;

        //r3 = *r0;
        r3 = r0->size;
        if(r3 <= r1) break;

        //r2 = r0 + 4;
        //r2 = r0->next;
        r0 = *(r0->next);
    }
    if(r3 <= r1)
    {
        //r3 = *(r0 + 4);
        /*r3 = _freelist->next;
        *r2 = r3;*/
        _freelist->size = r1;
        _freelist = _freelist->next;
        return &;
    }
    else
    {
        /*r4 = r3 - r1;
        r3 = r0 + r1;
        r5 = *(r0 + 4);
        *r3 = r4;
        r3 +=4 ;
        *(r3 + 4) = r5;
        r3 +=4 ;
        r3 -= 8;// 减8为了回到r3开始*/

        Node* lst = (void*)_freelist + r1;
        lst->size = _freelist->size - r1;
        lst->next = _freelist->next;

        _freelist->size = r1;
        _freelist = lst;

        return &lst->next;
    }

    return r0;
}


使用特权

评论回复
jasontu| | 2024-5-2 17:26 | 显示全部楼层
可以参考freestos malloc/free作法, 在stack上面管理内存,会比较保险。

使用特权

评论回复
biechedan| | 2024-5-3 14:01 | 显示全部楼层
设置堆的大小为1024字节,你可以在“Heap Size”选项中输入“1024”。

使用特权

评论回复
phoenixwhite| | 2024-5-4 16:13 | 显示全部楼层
修改Heap_Size的值,将其设置为所需的堆大小。

使用特权

评论回复
minzisc| | 2024-5-4 22:34 | 显示全部楼层
需要理解堆和栈在嵌入式系统中的作用和区别。堆是用于动态内存分配的区域,而栈则用于函数调用和局部变量。

使用特权

评论回复
benjaminka| | 2024-5-6 14:07 | 显示全部楼层
当使用动态内存分配时,很容易发生内存泄漏。确保你的代码正确管理内存,避免泄漏,否则可能会导致堆耗尽并影响系统的稳定性。

使用特权

评论回复
benjaminka| | 2024-5-6 18:03 | 显示全部楼层
链接器脚本(如.ld文件)可以控制内存布局,包括堆和栈的大小。你可以修改链接器脚本来设置更大的堆区域,以满足你的应用程序需求。

使用特权

评论回复
linfelix| | 2024-5-7 14:42 | 显示全部楼层
如果你需要更精细地控制堆的使用,你可以编写自定义的堆管理代码。这涉及到替代MicroLIB提供的默认堆管理模块,并实现你自己的内存分配和释放函数(如malloc和free)。

使用特权

评论回复
louliana| | 2024-5-8 19:04 | 显示全部楼层
在“Misc Controls”选项下找到“Use MicroLIB”选项,勾选上。
在下方的“Heap Size”输入框中输入所需的堆大小值。

使用特权

评论回复
Henryko| | 2024-5-8 20:48 | 显示全部楼层
use microlib就可以了吗

使用特权

评论回复
maudlu| | 2024-5-9 12:42 | 显示全部楼层
查阅Keil的MicroLIB文档,了解如何配置和使用堆管理模块。这可能会提供一些关于如何设置堆大小的指导或建议。

使用特权

评论回复
ccook11| | 2024-5-9 20:54 | 显示全部楼层
MicroLIB将使用新设置的堆大小。

使用特权

评论回复
usysm| | 2024-5-10 15:05 | 显示全部楼层
默认情况下,Heap和Stack的大小都是由MicroLIB库自动分配的。如果你想手动设置堆的大小,可以取消勾选Automatic Heap Size Configuration,并在下面的Heap Size(KB)框中输入你想要的堆大小。

使用特权

评论回复
updownq| | 2024-5-10 22:26 | 显示全部楼层
在修改堆设置或编写自定义堆管理代码后,进行全面的测试以确保系统的稳定性和性能。使用内存分析工具来检测内存泄漏和其他与内存相关的问题,并进行必要的优化。

使用特权

评论回复
linfelix| | 2024-5-12 16:26 | 显示全部楼层
在设置堆大小时,确保分配足够的内存以满足你的应用程序需求

使用特权

评论回复
呐咯密密| | 2024-5-13 13:37 | 显示全部楼层
自动设置吗。应该是手动调节吧

使用特权

评论回复
nomomy| | 2024-5-13 20:21 | 显示全部楼层
如果堆大小设置得过小,可能会导致内存分配失败。同时,确保堆大小不会超过MCU的可用内存空间。

使用特权

评论回复
bestwell| | 2024-5-14 19:12 | 显示全部楼层
:在Keil的编译器设置中,可能有与内存分配和堆管理相关的选项。检查这些设置,看是否有任何可以调整堆大小的选项。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

1389

帖子

1

粉丝