3

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

  • _ebss 是可分配内存的开始,所有具有低地址然后 _ebss 是常量、程序指令等。它在链接描述文件中定义。开始时的值为 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 的文档(我担心这是棘手的部分),那么 arena 表示从 _sbrk 请求的堆数量。这是有道理的,因为第一个打印的 heap_end 和最后一个打印的 heap_end 之间的差异确实是 1816。fordblks 应该是指可用堆的总量,因此仍然可以从 _sbrk 请求的最大堆量。这是不正确的。从最后一个差异中可以看出,heap_end 和 MSP 之间的差异是 28K。当然,我们希望在两者之间保留一个缓冲区,以便 MSP 可以在不破坏所有内容的情况下增长,但我们希望提供超过 2 个。

当我让程序运行更长时间时,这成为一个问题:最终 fordblks 将接近零,malloc 将开始返回 NULL,而 heap_end 还没有达到 MSP。所以 malloc 拒绝在需要之前提供更多的内存方式。我该如何解决这种行为?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 告诉我时,我试图 malloc 1024 字节的 RAM NULL

编辑: 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 与实际的空堆无关,我不知道它基于什么。因为如果您在 while true 循环中 malloc 很多 100 字节,mallocing 将继续,直到 _sbrk 返回 -1,这是预期的行为。在某些情况下,malloc 将返回 NULL 而没有实际填充堆。一个示例是 malloc(1024) 返回 NULL,但允许使用五个 malloc(555)。所以它似乎与 Malloc 的内部结构有关。

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

4

1 回答 1

2

雪龙,

  1. 通常,如果系统内存量低或负载高,则尽可能避免任何动态分配。我建议查看诸如内存池之类的技术。
  2. 内存管理器在长时间运行的系统上总是存在碎片问题。块有对齐。例如,我见过内存管理器为任何东西分配至少 48 个字节。并且没有内存管理器可以公平地计算您的主题区域以及内存使用模式。

所以我的建议是避免动态内存分配并使用特定于您要求的对象池。对于几乎所有东西都有“池”、“循环缓冲区”等的嵌入式系统来说,这几乎是 100% 的情况。希望这个建议能以某种方式帮助你。

于 2015-01-04T12:52:12.913 回答