为什么我们不应该在嵌入式系统中拥有不同大小的动态分配内存

时间:2014-01-26 22:58:54

标签: memory-management embedded dynamic-memory-allocation static-memory-allocation

我在嵌入式系统中听说过,我们应该使用一些预先分配的固定大小的内存块(比如好友内存系统?)。有人能给我一个详细的解释原因吗? 谢谢,

3 个答案:

答案 0 :(得分:11)

在嵌入式系统中,内存非常有限。因此,如果你偶尔会丢失一个字节的内存(因为你分配了它,但你没有释放它),这将很快耗尽系统内存(1 GB的内存,泄漏率为1 /小时将占用它时间。如果你有4kB RAM,那就不长了)

基本上,避免动态内存的行为是为了避免程序中的错误的影响。由于静态内存分配是完全确定的(而动态内存分配不是),通过仅使用静态内存分配,可以抵消此类错误。其中一个重要因素是嵌入式系统通常用于安全关键应用程序。几个小时的停机可能会花费数百万美元,或者可能发生事故。

此外,取决于动态内存分配器,不确定性也可能需要不确定的时间,这可能导致更多的错误,尤其是在依赖于紧密计时的系统中(感谢Clifford提及此)。这种类型的错误通常难以测试和重现,因为它依赖于非常具体的执行路径。

此外,嵌入式系统通常没有MMU,因此没有内存保护。如果内存不足并且处理该条件的代码不起作用,最终可能会执行任何内存作为指令(可能会发生错误!但这种情况只与动态内存分配间接相关)。

Hao Shen所述,碎片化也是一种危险。它是否可能发生取决于您的确切用例,但在嵌入式系统中,由于碎片,很容易丢失50%的RAM。如果您分配的块总是具有完全相同的大小,则只能避免碎片。

性能也起作用(取决于用例 - 感谢Hao Shen)。静态分配的内存由编译器分配,而malloc()和类似的内存需要在设备上运行,因此消耗CPU时间(和功率)。

许多嵌入式操作系统(例如ChibiOS)支持某种动态内存分配器。但使用它只会增加发生意外问题的可能性。

请注意,通常使用较小的静态分配内存池来规避这些参数。这不是一个真正的解决方案,因为在这些池中仍然会耗尽内存,但它只会影响系统的一小部分。

正如Stephano Sanfilippo所指出的,某些系统甚至没有足够的资源来支持动态内存分配。

注意:大多数编码标准,包括the JPL coding standard DO-178B (对于关键的航空电子设备代码 - 感谢[Stephano Sanfilippo])({{3} })禁止使用malloc。

由于https://stackoverflow.com/users/2344584/stefano-sanfilippo,我还假设MISRA C standard禁止malloc() - 但我无法访问标准本身。

答案 1 :(得分:5)

这里不使用动态堆内存分配的主要原因基本上是:

a)决定论和相关的, b)内存碎片。

内存泄漏在这些小型嵌入式应用程序中通常不是问题,因为它们将在开发/测试的早期检测到。

然而,内存碎片可能变得不确定,导致(最佳情况)随机时间内的内存不足错误以及现场应用程序中的点。

在使用动态分配进行开发期间预测应用程序的实际最大内存使用量也可能是非常重要的,而静态分配内存的数量在编译时是已知的,检查内存是否可以是绝对无关紧要的。是否由硬件提供。

答案 2 :(得分:3)

从固定大小的池中分配内存比动态内存分配有一些优势。它可以防止堆碎片,并且更具确定性。

通过动态内存分配,动态大小的内存块从固定大小的堆中分配。分配不一定按照分配的顺序释放。随着时间的推移,这可能导致堆的空闲部分在堆的分配部分之间被划分的情况。随着这种碎片的发生,满足更大内存分配的请求变得更加困难。如果发出了大量内存分配的请求,并且堆中没有足够大的连续空闲段,则分配将失败。堆可能有足够的总可用内存,但如果它全部碎片且没有连续的部分,那么分配将失败。由于堆碎片而导致malloc()失败的可能性在嵌入式系统中是不可取的。

打击碎片的一种方法是将较小的内存分配重新加入更大的连续部分,因为它们被释放。这可以通过各种方式完成,但它们都需要时间并且可以使系统具有更低的确定性。例如,如果内存管理器在释放内存分配时扫描堆,那么free()完成所花费的时间可能会有所不同,具体取决于正在释放的分配的内存类型。这在许多嵌入式系统中是不确定的,也是不可取的。

从固定大小的块池中分配不会导致碎片。只要有一些空闲块,那么分配就不会失败,因为每个块都是正确的大小。另外,分配和释放固定大小的池更简单。因此,可以将allocate和free函数写成确定性的。