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

交流内存模型如下:

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

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

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

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

如果没有,并且假设您的意思是裸机,那么它取决于您的板级支持包中的C库实现。 该库必须提供brk(2)一些实现或具有类似效果的函数。 mallocbrksbrk设置的内存区域内工作。 例如,请参阅malloc源调用宏MORECORE ,默认情况下为sbrk 。 您的C库必须使用除内核调用之外的其他内容:)。

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

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

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

并且你有责任防止堆栈溢出!

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在链接_eheap中定义:

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

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

如何防止堆栈与堆重叠?

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

1)裸金属你可能不想首先使用malloc

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

3)即使在非裸机(windows,linux等)上堆栈命中堆或运行到代码空间也不是你通常得到保护的东西,你需要告诉编译器,如果它支持它,要添加更多的代码,或者你只是设计你的软件不会发生冲突。

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

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

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

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

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

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

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