Newlib的Malloc是"忽略"一大堆免费记忆

时间:2015-01-03 15:06:55

标签: c memory malloc bare-metal newlib

所以我使用裸机malloc与自编的_sbrk结合使用。我在Stellaris Launchpad上运行一切。该板包含皮质M4。它还包含32K RAM,从memaddr 0x20000000开始,一直运行到0x20007FFF。 在项目一开始,情况如下:

  • _ebss是可分配内存的开始,然后_ebss具有较低地址的所有内容都是consts,程序指令等。它在链接描述文件中定义。 start的值是0x200008b0
  • 在软件的加载阶段,主堆栈指针设置为0x20007FFF,堆栈增长。这意味着,在一开始,大约有30,5K RAM可用于分配。

_sbrk希望分发这个RAM并确认正确数量的空闲RAM:在初始化阶段之后(这意味着堆栈增长了一点,因此可用RAM的数量更少),存在以下mem打印输出:

(S) MSP: 0x20007f4c, heap_end 0x200008b0, diff 30364
(F) MSP: 0x20007f4c, heap_end 0x200008e8, diff 30308
(S) MSP: 0x20007f4c, heap_end 0x200008e8, diff 30308
(F) MSP: 0x20007f4c, heap_end 0x20000fc8, diff 28548
arena: 1816
ordblks: 1
smblks: 0
hblks: 0
hblkhd 0
usmblks: 0
fsmblks: 0
uordblks: -1056
fordblks: 2872
keepcost: 2872

前四行来自_sbrk,最后两行来自mallinfo()。 (S)表示_sbrk的开头(在移动堆指针之前)和(F)表示在移动堆指针之后。 如果我能正确阅读mallinfo的文档(恐怕这是一个棘手的部分),那么竞技场意味着从_sbrk请求的堆数量。这是有道理的,因为第一个打印的heap_end和最后打印的heap_end之间的差异确实是1816.而dblks应该意味着堆释放的总量,因此仍然可以从_sbrk请求的最大堆量。这是不正确的。从最后一个diff可以看出,heap_end和MSP之间的差异是28K。我们希望在两者之间保持缓冲,以便MSP能够在不破坏一切的情况下成长,但是我们想要提供超过2.8K RAM的堆。

当我让程序运行一段时间时,这就成了一个问题:最终fordblks将接近零,malloc将开始返回NULL,而heap_end尚未通过远程攻击到达MSP。所以malloc拒绝在需要之前提供更多的mem方式。我该如何解决这个问题呢?什么是基于fordblks的值?

(S) MSP: 0x20007f34, heap_end 0x20000fc8, diff 28524
(F) MSP: 0x20007f34, heap_end 0x20001fc8, diff 24428
(S) MSP: 0x20007f34, heap_end 0x20001fc8, diff 24428
(F) MSP: 0x20007f34, heap_end 0x20002000, diff 24372
Could not create process timeoutProc30s
Test prepared
arena: 8304
ordblks: 2
smblks: 0
hblks: 0
hblkhd 0
usmblks: 0
fsmblks: 0
uordblks: 7648
fordblks: 656
keepcost: 112

修改 更多信息!上面你看到了Malloc说不的实际时刻:它在Could not create process..线。正如您可以清楚地看到的那样,正好在Malloc上方最后一次尝试从_sbrk获取堆,并且_sbrk不得不(堆指针已更改)。还有24K的空RAM。当malloc告诉我NULL时,我尝试使用malloc 1024字节的RAM。

修改 linker_script.ld文件:

_stack_buffer = 128; /*128 byte buffer between HEAP and STACK*/

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
    SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}

SECTIONS
{
    .text :
    {
        _text = .;
        KEEP(*(.isr_vector))
        *(.text*)
        *(.rodata*)
        _etext = .;
    } > FLASH

    .data : /*AT(ADDR(.text) + SIZEOF(.text))*/
    {
        _data = .;
        *(vtable)
        *(.data*)
        _edata = .;
    } > SRAM AT > FLASH

    .bss : AT (ADDR(.data) + SIZEOF(.data))
    {
        _bss = .;
        *(.bss*)
        *(COMMON)
        _ebss = .;
        _end = .;
    } > SRAM

    _stack_top = ORIGIN(SRAM) + LENGTH(SRAM) - 1; /*The starting point of the stack, at the very bottom of the RAM*/
}

修改 我做了一些关于这个主题的研究,我发现了一些有趣的东西。首先:fordblks与实际的空堆无关,我不知道它是基于什么的。因为如果你在一个真正的循环中malloc很多100个字节,mallocing将继续,直到_sbrk返回-1,这是预期的行为。在某些情况下,malloc将返回NULL而不会实际填充堆。一个示例是malloc(1024)返回NULL的位置,但允许使用5个malloc(555)。所以它似乎与Malloc的内部结构有关。

免责声明:上次我问了一个裸机问题,我被嘲笑为傲慢:我不是说newlib Malloc做错了什么我怀疑我需要在链接器脚本中定义一些东西来解决这个问题。我知道所有这些都是我的错,我在这里问为什么这个问题在这里以及我如何修复我的代码来修复这种行为。并不是说Newlib的人不知道他们在做什么。

1 个答案:

答案 0 :(得分:2)

谢龙,

  1. 通常,如果系统内存量较少或负载较高,则尽可能避免动态分配。我建议您查看memory pools等技术。
  2. 内存管理器在长时间运行的系统上总是存在碎片问题。块具有对齐。例如,我见过内存管理器为任何事情分配至少48个字节。并且没有内存管理器可以公平地考虑您的主题领域以及内存使用模式。
  3. 所以我的建议是避免动态内存分配并使用特定于您需求的对象池。对于嵌入式系统来说几乎是100%的情况,几乎所有的东西都有“池”,“cyrcular缓冲区”等等。希望这个建议能以某种方式帮助你。