c - 当超出堆(裸机)时,malloc() 不会返回 null

标签 c malloc heap-memory bare-metal stack-memory

我正在使用 Cross ARM GCC 工具链在 Kinetis TWRK70M120 上调试裸机项目。 (Kinetis Design Studio)

我有一个问题:
为什么 malloc() 在超出堆大小时不返回 NULL 指针?
这怎么可能?

我没想到会这样,但在分析了不同的情况后发现,只有当没有足够的空间时,malloc()才会返回NULL指针在堆中或堆栈中。我在 StackOverflow 论坛上发现了这个问题:

When does malloc return NULL in a bare-metal environment?

他们正在讨论 HEAP 和 STACK 冲突,什么可能与这个问题有关。虽然,我不确定我们是否可以谈论冲突,即使两个(堆栈和堆)地址范围都被正确管理(在我看来)。例如,如果我定义一个包含 10 个整数的本地数组,它将占用堆栈顶部的 cca 40-48 个字节。这意味着这部分堆栈不可用于动态分配,并且仅当您尝试分配大于 cca HEAP+STACK-48 字节的地址空间时,malloc() 返回 NULL。在我的项目中 0x400 + 0x500 - 48bytes(提到的数组) - 其他局部变量。我真的不确定这是否是常见行为,或者我只是没有正确理解 malloc() 函数。

这是我用于测试 malloc 函数的代码。
堆大小 = 0x20000400
堆大小 = 0x20000500
这是我用来测试 malloc 函数的代码。

#include "MK70F12.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

extern uint32_t __HeapBase;
extern uint32_t __HeapLimit;
extern uint32_t __StackTop;
extern uint32_t __StackLimit;

//MY TEST
static int j=0, k=0, i=0;   //Global variables

int main(void)
{
    //For testing purposes I defined STACK = 0x0x00000500 (cca 1280bytes) and
                                    //HEAP = 0x0x00000400 (cca 1024bytes) what means that
    //I have approximately (theoretically) 2304 bytes available.

    /* Write your code here */
     void* pHeapBase = ((void*)&__HeapBase);
     void* pHeapLimit= ((void*)&__HeapLimit);
     void* pStackTop = ((void*)&__StackTop);
     void* pStackLimit= ((void*)&__StackLimit);
     //---------------------------------------------------------------------------------------------------------
     char* pMalloc_data1=(char*)malloc(1200*sizeof(char));  //Dynamically allocated array which size is bigger
                                                        //than the HEAP size (beginning address in HEAP space).
                                                        //It seems, when it overruns the heap size it doesn't
                                                        //return NULL, but it continues growing into the stack.
                                                        //Malloc returns NULL pointer only if there is
                                                        //no space available neither in the HEAP nor in the STACK.
     if (pMalloc_data1==NULL)
     {
         return(1);                     //Allocation failed
     }
     for(j=0;j<1200;j++)
     {
         *(pMalloc_data1+j)='a'; //just some initialization with character
                                        //memory filled with 1200 chars
                                        //address range 0x200000a8 - 0x20000559(0x20000558?)
     }
     //---------------------------------------------------------------------------------------------------------

     int *pMalloc_data2 = (int*)malloc(10*sizeof(int)); //Dynamically allocated array
                                                        //(beginning address in STACK space)!!!.
                                                        //Malloc returns NULL pointer only if there is
                                                        //no space available in the STACK (HEAP is already full).
     if (pMalloc_data2==NULL)
     {
         return(1);
     }
     for(i=0;i<10;i++)
     {
         *(pMalloc_data2+i)=i+4000;     //memory filled with 200 integers
                                        //address range 0x20000560 - (theoretically)0x20000588 (0x20000590?)
     }

     int array[10] = {10,15,78,62,80,6528,8,58,64,984};   //Local static array => in STACK
                                                          //For this array is space in STACK reserved
                                                          //at the beginning of the program
     free(pMalloc_data1);
     free(pMalloc_data2);
     for(;;)
     {
         k++;
     }
    return 0;
}

最佳答案

在裸机环境中,您需要实现 _sbrk功能如下:

#include <unistd.h>
#include <errno.h>

// start and end of Heap as defined in the linker script (_heap_start < _heap_end)
extern unsigned int const _heap_start;
extern unsigned int const _heap_end;


/**
 * @brief                   Changes data segment space allocation (it may be called by malloc) .
 * @details                 It may be called by malloc() .
 * @param[in]   increment   Number of byte to be allocated .
 * @return                  On success the previous program break (program break = beginning of available mem) .
                            On error -1 .
 */
void *_sbrk(ptrdiff_t increment)
{
    static void *heap_free = 0;
    void *heap_prev;

    if  (heap_free == 0)
    {
        heap_free = (void *) &_heap_start;      // First call
    }

    if ((heap_free + increment) < ((void *) &_heap_end))
    {
        heap_prev = heap_free;
        heap_free += increment;
        return heap_prev;
    }
    else
    {
        errno = ENOMEM;
        return ((void *) -1);
    }
}

在 K70 上,堆栈向下增长,而堆向上增长。有了这个功能,只要设置_heap_end = _stack_end就永远不会发生碰撞。在你的链接器脚本中,并且你的堆栈大小足够大。

当您拥有操作系统时,_sbrk由它提供。

关于c - 当超出堆(裸机)时,malloc() 不会返回 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41977122/

相关文章:

c - brk/sbrk 的不安全/遗留问题是什么?

c - C中的内存分配/释放

out-of-memory - Alfresco GC 开销超出限制

c - 在选择阻塞时向 fd_Set 添加新的 FD

c - 如何实时运行无限循环 - Linux?

c++ - 检测多个枚举项何时映射到相同值

java - PermGen和Heap,区别及其意义

c - 通过 XBee 从 Arduino 传输数据

c - malloc 未定义

c - #include 会增加 RAM 使用吗?