realloc() 在嵌入式系统中安全吗?
Posted
技术标签:
【中文标题】realloc() 在嵌入式系统中安全吗?【英文标题】:Is realloc() safe in embedded system? 【发布时间】:2011-11-03 12:30:12 【问题描述】:在为嵌入式系统开发软件时,我多次使用realloc()
函数。现在有人说我“不应该在嵌入式中使用realloc()
”,没有任何解释。
realloc()
对嵌入式系统有危险吗?为什么?
【问题讨论】:
你为什么不问问是谁告诉你的? 如今嵌入式是一个非常广泛的领域。 @cnicutar 如果我可以问他们,那么我永远不会在这里问这个..!! @Mr.32 我的意思是你不应该接受没有争论的意见。 @cnicutar 你是对的。我和他们争论过,但他们刚刚说 realloc 对嵌入式是危险的,所以不要使用。我只是开发人员,所以我必须跟随他们..! 【参考方案1】:是的,所有动态内存分配都被认为是危险的,并且大多数“高完整性”嵌入式系统都禁止使用它,例如工业/汽车/航空航天/医疗技术等。您问题的答案取决于哪种类型你正在做的嵌入式系统。
它被高完整性嵌入式系统禁止的原因不仅是潜在的内存泄漏,还有许多与这些功能相关的危险的未定义/未指定/impl.defined 行为。
编辑:我也忘了提到堆碎片,这是另一个危险。此外,MISRA-C 还提到了“数据不一致、内存耗尽、非确定性行为”作为不应使用它的原因。前两者似乎相当主观,但非确定性行为在这类系统中绝对是不允许的。
参考文献:
MISRA-C:2004 规则 20.4“不得使用动态堆内存分配。” IEC 61508 功能安全,61508-3 附录 B(规范)表 B1,>SIL1:“无动态对象”,“无动态变量”。【讨论】:
这是唯一好的答案。你永远不应该在嵌入式系统中使用堆内存。将所有数据放在数据部分、bss 或堆栈中。这样你就有了一个固定大小的内存占用,你可以计算你的内存使用量。这样您就可以确定您实际使用了多少内存,并且永远不会超过该限制。使用堆是导致嵌入式系统在运行较长时间后崩溃的最常见载体。 我理解这个问题尤其与 realloc() 有关,与是否使用动态内存分配无关。 @DipSwitch:现在我们已经有了 64 兆甚至半 GB 内存的“嵌入式系统”,“从不”不再适用。 @pmod 问题是 realloc() 是否危险。答案是肯定的,就像任何形式的动态内存分配一样。 @Frederico 如果您在启动时调用 malloc 并且不再调用,为什么不首先静态分配内存?【参考方案2】:在许多嵌入式系统中,自定义内存管理器可以提供比 malloc/realloc/free 更好的语义。例如,某些应用程序可以使用简单的标记和释放分配器。保持一个指向尚未分配的内存开头的指针,通过向上移动指针来分配事物,并通过将指针移动到它们下方来抛弃它们。如果有必要抛弃一些东西,同时保留在它们之后分配的其他东西,那么这是行不通的,但是在不需要的情况下,标记和释放分配器比任何其他分配方法都便宜。在某些情况下,mark-and-release 分配器不够好,从堆的开头分配一些东西,从堆的末尾分配一些东西可能会有所帮助。可以释放从一端分配的东西而不影响从另一端分配的东西。
在非多任务或协作多任务系统中有时可能有用的另一种方法是使用内存句柄而不是直接指针。在一个典型的基于句柄的系统中,有一个所有已分配对象的表,构建在内存的顶部向下工作,对象本身是自下而上分配的。内存中每个分配的对象要么持有对引用它的表槽的引用(如果是活动的),要么是其大小的指示(如果是死的)。每个对象的表条目将保存对象的大小以及指向内存中对象的指针。可以通过简单地找到一个空闲的表槽来分配对象(很容易,因为表槽都是固定大小的),在空闲内存的开头存储对象的表槽的地址,在此之外存储对象本身,并更新开始的可用内存指向刚刚过去的对象。可以通过用长度指示替换反向引用并释放表中的对象来释放对象。如果分配失败,则从内存顶部开始重新定位所有活动对象,覆盖所有死对象,并更新对象表以指向它们的新地址。
这种方法的性能是不确定的,但碎片化不是问题。此外,在某些协作式多任务系统中,可能会在“后台”执行垃圾收集;如果垃圾收集器可以在通过松弛空间所需的时间内完成一次传递,就可以避免长时间的等待。此外,可以使用一些相当简单的“世代”逻辑来提高平均情况下的性能,但会牺牲最坏情况下的性能。
【讨论】:
【参考方案3】:realloc() 在嵌入式系统中的问题与任何其他系统没有什么不同,但在内存更受限制且失败的后果更难以接受的系统中,后果可能更为严重。
到目前为止没有提到的一个问题是 realloc()(以及任何其他与此相关的动态内存操作)是非确定性的;那就是它的执行时间是可变的和不可预测的。许多嵌入式系统也是实时系统,在这样的系统中,非确定性行为是不可接受的。
另一个问题是线程安全。检查您的库的文档以查看您的库对于动态内存分配是否是线程安全的。通常,如果是这样,您将需要实现互斥存根以将其与您的特定线程库或 RTOS 集成。
并非所有嵌入式系统都是相似的;如果您的嵌入式系统不是实时的(或者所讨论的进程/任务/线程不是实时的,并且独立于实时元素),并且您有大量未使用的内存或虚拟内存功能,如果在大多数情况下可能不明智,那么使用 realloc() 可能是可以接受的。
与其接受“传统智慧”而不管动态内存,您应该了解您的系统要求,以及动态内存功能的行为并做出适当的决定。也就是说,如果您正在为尽可能广泛的平台和应用程序构建可重用性和可移植性的代码,那么重新分配可能是一个非常糟糕的主意。例如,不要将其隐藏在库中。
还要注意,当容器容量增加时,C++ STL 容器类也存在同样的问题,它们会动态重新分配和复制数据。
【讨论】:
【参考方案4】:realloc
可能会失败,就像 malloc
会失败一样。这就是为什么您可能不应该在嵌入式系统中使用它们的原因之一。
realloc
比malloc
更糟糕,因为您需要在realloc
期间使新旧指针有效。换句话说,您将需要 2 倍于原始 malloc
的内存空间,再加上任何额外的数量(假设 realloc
正在增加缓冲区大小)。
使用realloc
会非常危险,因为它可能会返回一个指向您的内存位置的新指针。这意味着:
realloc
之后更正对旧指针的所有引用。
对于多线程系统,realloc
必须是原子的。如果您禁用中断来实现此目的,realloc
时间可能足够长,会导致看门狗硬件复位。
更新:我只是想说清楚。我并不是说realloc
比使用malloc
/free
实现realloc
更糟糕。那也一样糟糕。如果你可以做一个 malloc
和 free
而不调整大小,它会稍微好一点,但仍然很危险。
【讨论】:
第 2 点和第 3 点是否不适用于手动调用malloc()
/free()
?
3.是一个好点,realloc 确实比 malloc 更危险,尽管 malloc 单独被认为太危险了。
@sharptooth - 对,realloc 在某些情况下只是在做 malloc/free。所以我的回答试图解释 realloc 不是魔法,也不是危险的。可能不清楚龙在哪里,除非使用 malloc/free API 实现 realloc。我只是想直接调出内存开销。【参考方案5】:
这取决于特定的嵌入式系统。开始在小型嵌入式系统上进行动态内存管理很棘手,但realloc
并不比free
和malloc
复杂(当然,它不是这样做的)。在某些嵌入式系统上,您一开始做梦都不会想到会调用malloc
。在其他嵌入式系统上,您几乎可以假装它是桌面。
如果您的嵌入式系统分配器不佳或 RAM 不多,那么realloc
可能会导致碎片问题。这就是为什么你也避免使用malloc
,因为它会导致同样的问题。
另外一个原因是一些嵌入式系统必须是高可靠性的,malloc
/realloc
可以返回NULL
。在这些情况下,所有内存都是静态分配的。
【讨论】:
我不是一个嵌入式的人,但你似乎在谈论 free 和 malloc 的切线。 Realloc 可能远不止这些,只是 interface 很简单。 我是说它并不比 malloc 和 free 更复杂,并不是说它是由它们组成的。如果我的措辞不准确而引起您的反对,我深表歉意。【参考方案6】:好吧,如果可能的话,最好避免使用 realloc,因为这个操作代价高昂,尤其是被放入循环中:例如,如果一些分配的内存需要扩展并且在当前块之后和下一个分配的块之间没有间隙block - 这个操作几乎等于:malloc + memcopy + free。
【讨论】:
realloc()
比 malloc()
/free()
差吗?
更糟糕的是,可以通过使用相邻的内存块来优化重新分配,但这当然并不总是可行的
@pmod:对realloc
的单独调用永远不会比malloc
/ memcpy
/ free
更糟糕,因为它总是可以这样做,它可能会消除memcpy
。
抱歉搞砸了否定,我的意思是“至少没有更糟,但统计上更好”,这取决于实施以上是关于realloc() 在嵌入式系统中安全吗?的主要内容,如果未能解决你的问题,请参考以下文章
# 2017-2018-1 20155224 《信息安全系系统设计基础》实验四
信息安全系15级学生课程设计——2018.05 小组课题报告
C语言中动态内存分配函数的用法及作用?(比如malloc,calloc,realloc等)