c - Newlib 的 Malloc 是 "ignoring"一大堆空闲内存

标签 c memory malloc bare-metal newlib

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

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

_sbrk 想要分发这个 RAM 并确认正确数量的空闲 RAM:在初始化阶段之后(这意味着堆栈已经增长了一点,所以空闲 RAM 的数量较低),存在以下内存打印输出:

(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.8K RAM 的堆。

当我让程序运行更长一点时,这就变成了一个问题:最终 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 告诉我 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 与实际的空堆无关,我不知道它基于什么。因为如果您在 while true 循环中 malloc 大量 100 个字节,mallocing 将继续直到 _sbrk 返回 -1,这是预期的行为。在某些情况下,malloc 会返回 NULL 而堆实际上并未被填充。一个示例是 malloc(1024) 返回 NULL,但允许五个 malloc(555)。所以它似乎与 Malloc 的内部结构有某种关系。

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

最佳答案

谢龙,

  1. 通常,如果系统内存量低或负载高,则尽可能避免任何动态分配。我建议看看像 memory pools 这样的技术.
  2. 内存管理器在长时间运行的系统上总是存在碎片问题。 block 具有对齐方式。例如,我见过内存管理器为任何东西分配至少 48 个字节。没有内存管理器可以公平地说明您的主题领域,进而说明内存使用模式。

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

关于c - Newlib 的 Malloc 是 "ignoring"一大堆空闲内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27756216/

相关文章:

java - 处理 controlP5 使用过多内存

java - 程序执行时间

memory - 调用堆栈会耗尽内存吗?

c++ - 修改 "__cxa_allocate_exception"没有使用malloc

c - malloc 运行时错误

c++ - 如何在多线程中关闭 malloc() 的 mmap 使用?

c - 在 C 代码中引用 BASH 变量

c - int 指针数组中的指针地址

C - while 循环内 isdigit() 调用后递增的奇怪行为

c - 在 C 中搜索字符串内的单词