malloc什么时候在裸机环境中返回NULL?

时间:2016-08-24 02:51:54

标签: c stack arm bare-metal

有一个c内存模型如下:

      +--------+   Last Address of RAM
      | Stack  |
      |   |    |
      |   v    |
      +--------+
RAM   |        |
      |        |
      +--------+
      |   ^    |
      |   |    |
      | Heap   |
      +--------+
      |   ZI   |
      +--------+
      |   RW   |  
      +========+  First Address of RAM

堆栈和堆空间以相反的方向增加。它们在中间会相互重叠。 所以我的问题是:

  • 在裸机环境中,malloc何时返回NULL?
  • 在裸机环境中,如何防止堆栈与堆重叠?

5 个答案:

答案 0 :(得分:3)

如果你使用静态的编译时内存布局,那么@WikiWang是正确的(编辑,尽管你必须以某种方式告诉你的malloc实现堆的末尾)。

如果没有,并假设您的意思是裸机,则取决于您的主板支持包中的C库实现。该库必须提供brk(2)的一些实现或具有类似效果的函数。 malloc适用于brksbrk设置的内存区域。例如,请参阅malloc source调用宏MORECORE,默认情况下为sbrk。你的C库必须使用除内核调用以外的东西:)。

答案 1 :(得分:2)

堆栈的大小在编译时确定。

      +--------+   Last Address of RAM
      | Stack  |
      |   |    |
      |   v    |
      +--------+
RAM   |        |
      +--------+   Stack Limit
      |        |
      +--------+
      |   ^    |
      |   |    |
      | Heap   |
      +--------+
      |   ZI   |
      +--------+
      |   RW   |  
      +========+  First Address of RAM

如果没有足够的空间用于请求的堆,则malloc返回null。

你有责任防止堆栈溢出!

答案 2 :(得分:2)

  

malloc什么时候返回NULL?

它可能依赖于C编译器和库实现。例如,我的malloc实现调用sbrk,这是Linux环境中的系统调用。由于我们在MCU上没有Linux,我提供了我自己的sbrk实现,如下所示:

// Global variables.
extern unsigned int _heap;
extern unsigned int _eheap;
static caddr_t heap = NULL;

caddr_t _sbrk(int incr)
{
  caddr_t prevHeap;
  caddr_t nextHeap;

  if (heap == NULL) { // first allocation
    heap = (caddr_t) & _heap;
  }

  prevHeap = heap;

  // Always return data aligned on a 8 byte boundary
  nextHeap = (caddr_t) (((unsigned int) (heap + incr) + 7) & ~7);
  if (nextHeap >= (caddr_t) & _eheap) {
    errno = ENOMEM;
    return ((void*)-1); // error - no more memory
  } else {
    heap = nextHeap;
    return (caddr_t) prevHeap;
  }
}

_eheap在链接描述文件中定义:

PROVIDE ( _eheap = ALIGN(ORIGIN(ram) + LENGTH(ram) - 8 ,8) );

有了这个,我的程序中的malloc只有在它到达内存结束时才会返回错误。堆栈顶部地址无法识别,因此我无法通过检查malloc返回值来诊断可能的堆栈损坏。

  

如何防止堆栈与堆重叠?

您可以定义自己的sbrk限制以返回错误,这将阻止malloc入侵堆栈内存。

答案 3 :(得分:2)

1)裸金属你可能不想在第一时间使用malloc

2)裸机你拥有那个记忆并管理它,所以你是唯一能回答这个问题的人。

3)即使在非裸机(windows,linux等)堆栈中遇到堆或运行到代码空间也不是你通常得到保护的东西,你需要告诉编译器,它是否支持它,添加更多代码,或者只是将软件设计为不碰撞。

你的内存分配方案是什么,你的代码做了什么,如果你告诉这段代码它可以分配什么空间怎么做。如果编译器完全支持它,您可能需要告诉编译器空间或运行时指示堆顶部当前的位置。或者编译器可能依赖于填充模式的空间,并且在分配之前检查模式,这意味着在裸机中需要使用该模式填充该内存。首先,您需要了解编译器输出是否以及如何在防止堆栈与数据或程序空间冲突方面起作用。

通常情况下,根据您的系统/软件设计,您知道堆栈的最深情况,以及您剩余的内存量。当您进行设计然后实施时,您需要验证最大堆栈深度,最终需要多少ram,并确保没有超额订阅该系统中该处理器可用的ram。

裸金属没有理由期望编译器,库或其他工具为你做这项工作。

答案 4 :(得分:1)

分配失败时,

malloc将返回NULL。通常这是由于缺乏记忆。在自由运行的模型中,堆和堆栈没有限制,它总是会给你内存(即使这意味着与堆栈冲突)。

幸运的是,这从未完成。通常堆和堆栈具有固定的最大大小,因此检查更容易。例如,如果库malloc调用sbrk(典型情况),则可以编写sbrk,以便拒绝将堆扩展到堆栈限制之外。这可以避免堆冲入堆栈。

另一种方式(堆栈碰撞到堆)比较棘手。首先,没有太多可以从堆栈溢出中恢复,因此这里的策略是生成有用的调试信息并停止系统以避免在损坏的内存上工作。从技术上讲,编译器可以为每个堆栈分配添加检查,但这会显着减慢代码速度(我甚至不知道是否有任何编译器支持它 - 可能是的,使用一些检测)。如果您的MCU具有内存保护单元(MPU),则可以在堆栈限制之上配置一个小的不可访问区域,以便堆栈溢出将产生MPU故障。这种技术通常称为保护页面(很像guard bytes带有内存断点,如果你愿意的话)。但是,为了实现这一点,异常需要使用不同的堆栈(否则您将在同一条船上)。请注意,stack canaries不能防止堆栈堆冲突。

话虽如此,在裸机是设计内存布局的那个,你可以根据自己的需要进行设置。 malloc通常不赞成,因为它是非确定性的。此外,您应该分析堆栈使用情况并了解最大堆栈深度,以便正确调整堆栈大小。