在为嵌入式系统开发软件时,我多次使用realloc()
函数。现在我被告知我“不应该在嵌入式中使用realloc()
而不做任何解释。
realloc()
对嵌入式系统有危险吗?为什么?
答案 0 :(得分:20)
是的,所有动态内存分配都被认为是危险的,并且它被禁止用于大多数“高完整性”嵌入式系统,例如工业/汽车/航空航天/医疗技术等等。您的问题的答案取决于什么类型你正在做的嵌入式系统。
它被高度完整性嵌入式系统禁止的原因不仅是潜在的内存泄漏,而且还有很多与这些函数相关的危险的未定义/未指定/ impl.defined行为。
编辑:我也忘了提到堆碎片,这是另一个危险。此外,MISRA-C还提到“数据不一致,内存耗尽,非确定性行为”作为不应该使用它的原因。前两者似乎相当主观,但非确定性行为绝对是这类系统所不允许的。参考文献:
答案 1 :(得分:5)
这取决于特定的嵌入式系统。小型嵌入式系统上的动态内存管理起初很棘手,但realloc
并不比free
和malloc
复杂(当然,这不是它的作用)。在某些嵌入式系统上,您从未想过首先调用malloc
。在其他嵌入式系统上,你几乎假装它是一个桌面。
如果您的嵌入式系统配置不佳或内存不足,那么realloc
可能会导致碎片问题。这也是你避免使用malloc
的原因,因为它会导致同样的问题。
另一个原因是某些嵌入式系统必须具有高可靠性,malloc
/ realloc
可以返回NULL
。在这些情况下,所有内存都是静态分配的。
答案 2 :(得分:4)
在许多嵌入式系统中,自定义内存管理器可以提供比malloc / realloc / free更好的语义。例如,某些应用程序可以使用简单的标记和释放分配器。保持指向尚未分配的内存的开始的指针,通过向上移动指针来分配内容,并通过移动它们下方的指针来抛弃它们。如果有必要抛弃一些东西,同时保留其后分配的东西,那将无法工作,但在没有必要的情况下,标记和释放分配器比任何其他分配方法便宜。在某些情况下,标记和释放分配器不够好,从堆的开头分配一些东西并从堆的末尾分配其他东西可能会有所帮助;一个人可以释放从一端分配的东西而不影响从另一端分配的东西。
有时在非多任务处理或协作多任务系统中有用的另一种方法是使用内存句柄而不是直接指针。在典型的基于句柄的系统中,有一个包含所有已分配对象的表,这些对象构建在向下工作的内存顶部,对象本身从下向上分配。内存中的每个已分配对象都包含对引用它的表槽的引用(如果是活动的)或者其大小的指示(如果已死)。每个对象的表条目将保存对象的大小以及指向内存中对象的指针。可以通过简单地找到一个空闲表槽来分配对象(简单,因为表槽都是固定大小),在可用内存的开头存储对象的表槽的地址,将对象本身存储在其之外,并更新开始可用内存指向刚刚过去的对象。可以通过用长度指示替换反向引用来释放对象,并释放表中的对象。如果分配失败,则重新定位从内存顶部开始的所有活动对象,覆盖任何死对象,并更新对象表以指向其新地址。
这种方法的表现是不确定的,但碎片不是问题。此外,在一些协作式多任务系统中可以“在后台”执行垃圾收集;如果垃圾收集器可以在通过松弛空间的时间内完成通过,则可以避免长时间等待。此外,一些相当简单的“世代”逻辑可用于改善平均情况性能,但代价是最坏情况下的性能。
答案 3 :(得分:2)
realloc
可能会失败,就像malloc
一样。这就是为什么你不应该在嵌入式系统中使用它的一个原因。
realloc
比malloc
更糟糕,因为您需要在realloc
期间使旧指针和新指针有效。换句话说,您需要原始malloc
的2倍内存空间,加上任何额外的数量(假设realloc
正在增加缓冲区大小)。
使用realloc
会非常危险,因为它可能会返回一个指向内存位置的新指针。这意味着:
realloc
。realloc
必须是原子的。如果要禁用中断来实现此目的,realloc
时间可能足够长,导致看门狗硬件重置。 更新:我只是想说清楚。我并不是说realloc
比使用realloc
/ malloc
实施free
更糟糕。那也差一点。如果您可以执行单个malloc
和free
,而无需调整大小,则会稍好一些,但仍然很危险。
答案 4 :(得分:1)
嵌入式系统中realloc()的问题与任何其他系统没有什么不同,但是在内存受到更多限制的系统中,后果可能更严重,并且故障的后续序列不太可接受。
到目前为止未提及的一个问题是realloc()(以及任何其他动态内存操作)非确定性;也就是说,它的执行时间是可变的,不可预测的。许多嵌入式系统也是实时系统,在这种系统中,非确定性行为是不可接受的。
另一个问题是线程安全问题。检查库的文档,看看您的库是否是线程安全的动态内存分配。通常如果是,您将需要实现互斥存根以将其与您的特定线程库或RTOS集成。
并非所有的emebdded系统都是相似的;如果您的嵌入式系统不是实时的(或者所讨论的进程/任务/线程不是实时的,并且独立于实时元素),并且您有大量未使用的内存或虚拟内存功能,然后使用realloc()可能是可以接受的,如果在大多数情况下可能是不明智的。
不管接受“传统智慧”和条形动态内存,你应该了解你的系统要求,以及动态记忆功能的行为并作出适当的决定。也就是说,如果您正在为尽可能广泛的平台和应用程序构建可恢复性和可移植性代码,那么重新分配可能是一个非常糟糕的主意。例如,不要将它隐藏在库中。
另请注意,在容器容量增加时,动态重新分配和复制数据的C ++ STL容器类存在同样的问题。
答案 5 :(得分:0)
嗯,如果可能的话,最好避免使用realloc,因为这个操作特别昂贵,特别是放入循环:例如,如果需要扩展一些分配的内存并且在当前块和下一个分配之后没有间隙block - 此操作几乎等于:malloc + memcopy + free。