在裸机环境中,malloc何时返回NULL?
有一个c内存模型,如下所示:在裸机环境中,malloc何时返回NULL?,c,stack,arm,bare-metal,C,Stack,Arm,Bare Metal,有一个c内存模型,如下所示: +--------+ Last Address of RAM | Stack | | | | | v | +--------+ RAM | | | | +--------+ | ^ | | | | | Heap | +--------+ |
+--------+ Last Address of RAM
| Stack |
| | |
| v |
+--------+
RAM | |
| |
+--------+
| ^ |
| | |
| Heap |
+--------+
| ZI |
+--------+
| RW |
+========+ First Address of RAM
堆栈和堆空间以相反的方向增加。它们在中间会相互重叠。
因此,我的问题是:
- 在裸机环境中,malloc何时返回NULL
- 在裸机环境中,如何防止堆栈与堆重叠
+--------+ Last Address of RAM
| Stack |
| | |
| v |
+--------+
RAM | |
+--------+ Stack Limit
| |
+--------+
| ^ |
| | |
| Heap |
+--------+
| ZI |
+--------+
| RW |
+========+ First Address of RAM
如果没有足够的空间用于请求的堆,则malloc返回null
防止堆栈溢出是您的职责 @WikiWang是正确的,如果您使用的是静态编译时内存布局(edit,尽管您必须以某种方式告诉您的
malloc
实现堆的末尾在哪里)
如果不是,假设您指的是裸机,这取决于板支持包中的C库实现。库必须提供一些实现或具有类似效果的函数malloc
在brk
或sbrk
设置的内存区域内工作。例如,请参阅调用宏MORECORE
,该宏默认为sbrk
。您的C库必须使用内核调用以外的其他方法来实现:)
malloc何时返回NULL
它可能取决于C编译器和库实现。例如,mymalloc
实现调用,这是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;
}
}
链接器脚本中定义了\u eheap
:
PROVIDE ( _eheap = ALIGN(ORIGIN(ram) + LENGTH(ram) - 8 ,8) );
有了这个,我的程序中的malloc
只有在到达内存末尾时才会返回错误。堆栈顶部地址无法识别,因此我无法通过检查malloc
返回值来诊断可能的堆栈损坏
如何防止堆栈与堆重叠
您可以为sbrk
定义自己的返回错误的限制,这将防止malloc
侵入堆栈内存。1)裸机您可能首先不想使用malloc
2) 裸机你拥有并管理内存,所以你是唯一能回答这个问题的人
3) 即使在非裸机(windows、linux等)上,堆栈命中堆或运行到代码空间也不是您通常可以得到保护的东西,您需要告诉编译器(如果它支持的话)添加更多的代码,或者您只需正确地设计软件以避免冲突
你的内存分配方案是什么,你的代码是做什么的,如果你告诉代码它可以从哪个空间分配,你会怎么做。如果编译器完全支持它,您可能需要告诉编译器空间或运行时指示堆的顶部当前所在的位置。或者,编译器依赖于用模式填充该空间,并在分配之前检查该模式,这意味着在裸机中,对于这样的情况,您需要用该模式填充该内存。首先,您需要了解编译器输出是否以及如何防止堆栈与数据或程序空间冲突
通常,通过您的系统/软件设计,您知道在最坏的情况下,您的堆栈会有多深,以及您还剩下多少内存。在进行设计和实现时,您需要验证最大堆栈深度、最终需要多少ram,并确保没有超额订购该系统中该处理器可用的ram
裸机没有理由期望编译器、库或其他工具为您完成这项工作。
malloc
在分配失败时将返回NULL。通常这是由于缺乏记忆。在自由运行的模型中,堆和堆栈没有限制,它总是会给您内存(即使这意味着与堆栈冲突)
幸运的是,这一点从未实现。通常,堆和堆栈具有固定的最大大小,因此检查更容易。例如,如果库malloc
调用您的sbrk
(典型情况),您可以编写sbrk
,使其拒绝将堆扩展到堆栈限制之外。这样可以避免堆碰撞到堆栈中
另一种方法(堆栈碰撞成堆)更为棘手。首先,要从堆栈溢出中恢复,您没有什么可做的,因此这里的策略是生成有用的调试信息并停止系统以避免使用损坏的内存。从技术上讲,编译器可以为每个堆栈分配添加检查,但这会显著降低代码的速度(我甚至不知道是否有编译器支持它-可能是的,使用一些工具)。如果MCU有内存保护单元(MPU),可以在堆栈限制之上配置一个小的不可访问区域,这样堆栈溢出将产生MPU故障。这种技术通常称为保护页(如果需要的话,很像使用内存断点)。但是,要使其正常工作,异常需要使用不同的堆栈(否则您将处于同一条船上)。请注意,不要防止堆栈冲突
也就是说,在裸机中,你是设计内存布局的人,你可以根据自己的需要随意设置<代码>malloc通常不受欢迎,因为它是不确定的。此外,您应该分析堆栈的使用情况,并了解最大堆栈深度,以便正确调整堆栈的大小。什么是“无操作系统”?您是指裸机中的“非操作系统”吗?是的,很抱歉造成混淆。在裸机环境中,通常最好不要使用
malloc
。相反,使用管理固定数量的固定大小内存块的内存池。在裸机环境中,malloc
通常不存在。如果它确实存在,那么检查文档,看看它来自哪里