5

我听说在嵌入式系统中,我们应该使用一些预先分配的固定大小的内存块(比如伙伴内存系统?)。有人能给我详细解释一下为什么吗?谢谢,

4

3 回答 3

13

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

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

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

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

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

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

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

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

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

注意:大多数编码标准,包括JPL 编码标准DO-178B(用于关键的航空电子代码 - 感谢Stephano Sanfilippo)都禁止使用 malloc。

由于这个论坛帖子,我还假设MISRA C 标准禁止- 但是我无权访问标准本身。malloc()

于 2014-01-26T23:02:06.723 回答
6

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

a)确定性和相关性,b)内存碎片。

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

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

在开发过程中使用动态分配预测应用程序的实际最大内存使用量也可能并非易事,而静态分配的内存量在编译时是已知的,检查内存是否可以由应用程序提供绝对是微不足道的。硬件与否。

于 2014-01-26T23:24:00.557 回答
4

从固定大小的块池中分配内存与动态内存分配相比有几个优点。它可以防止堆碎片并且更具确定性。

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

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

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

于 2014-01-26T23:53:44.540 回答