1

我目前正在使用 STM32CubeMx 和 Keil uVision 为 STM32F407 开发应用程序。我知道嵌入式系统中的动态内存分配大多是不鼓励的,但是在互联网上我可以找到一些支持它的论据。

由于我的发明家灵魂,我想尝试去做,但要安全地去做。假设我正在为传入的 UART 消息创建一个动态分配的 fifo,其中包含由 msg 本身及其长度组成的结构。但是我不想这样做消耗所有的堆大小,因此我想检查我还剩下多少:我的新(?)想法是尝试临时分配一些大块内存(比如 100 个字符)-如果成功,我接受传入的消息,如果不是 - 这意味着我的堆用完了并忽略消息(或接受它并将最旧的消息出队)。检查后我当然释放了临时内存。

我的脑海中出现了几个问题:

  1. 首先,这有意义吗?根据您的经验,您认为它有用且安全吗?
  2. 我找不到关于在 ES 中究竟共享 RAM 的确切信息(我知道堆、堆栈和 volatile 变量)所以我的问题是:提供对 1 的答案不是“地狱不回家”,什么大小您会为提到的控制器选择临时内存检查器吗?
  3. 关于微本身 - 它有 192kB RAM,但是在Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm\startup_stm32f407xx.s文件中,只有 512B+1024B 分配给堆和堆栈 - 不是很少,留下百日咳,剩余 190kB 用于 volatile vars?将堆大小增加到 50kB 是否明智?如果是,我是直接在这个文件中做,还是在其他地方做更好的做法?

可能对你们中的一些人来说,“安全动态记忆”和“嵌入”在一篇文章中既令人震惊又令人眼花缭乱,但请记住,这是在试验和探索新的视野 :) 谢谢和问候。

4

2 回答 2

1

Keil uVision 仅描述 IDE。如果您使用的是 KEil MDK-ARM,这意味着 ARM 的 RealView 编译器,那么您可以使用该__heapstats()函数获取准确的堆信息。

__heapstats()有点奇怪,它不是简单地返回一个值,而是将堆信息输出到一个格式化的输出流,该输出流由传递给它的函数指针和文件描述符提供便利。输出函数必须有一个fprintf()相似的接口。您当然可以使用fprintf(),但这需要您正确地重新定位 stdio

例如以下:

typedef int (*__heapprt)(void *, char const *, ...);
__heapstats( (__heapprt)fprintf, stdout ) ; 

输出例如:

4180 bytes in 1 free blocks (avge size 4180)
1 blocks 2^11+1 to 2^12

不幸的是,这并不能真正实现您所需要的,因为它会输出文本。但是,您可以实现自己的函数来捕获内存中的数据并解析结果。您可能只需要捕获第一个十进制数字字符并丢弃其他任何内容,除了可用内存量和最大可分配块当然不一定是同一件事。碎片由数量或空闲块及其平均大小表示。您也许可以保证能够分配至少一个平均大小的块。

嵌入式系统中动态分配的问题与处理内存耗尽有关,在实时系统中,使用默认的 malloc/free 实现的分配和释放的非确定性时序。在您的情况下,您最好使用固定块分配器。您可以通过创建内存块的静态数组(或通过在启动时从堆中动态分配它们)并将指向每个块的指针放置在队列或链表或堆栈结构上来实现这样的分配器。要分配您只需从队列/列表/堆栈中删除一个指针,然后释放您将指针放回。当可用块结构为空时,内存耗尽。它完全是确定性的,因为它是您的可以轻松监控实施的性能和容量。

关于问题 3。您需要调整堆和系统堆栈大小以适合您的应用程序。我使用的大多数工具都有一个链接器脚本,它会自动将所有可用内存分配给非静态分配、分配给堆栈或为其他目的保留给堆的所有可用内存。但是 MDK-ARM 在默认链接描述文件中并没有这样做,而是分配一个固定大小的堆。

您可以使用链接器映射文件摘要来确定有多少空间未使用并手动扩展堆。当静态分配的数据量可能增加时,我通常会留下少量未使用的空间来进行维护。然而,在某些时候;您最终会耗尽内存,并且来自链接器的神秘错误消息可能不会明显表明您的堆太大。可以覆盖默认链接器脚本并提供您自己的,毫无疑问可以自动调整堆大小 - 尽管我从未尝试过。

于 2015-06-18T09:06:47.320 回答
1

好的,我已经通过动态堆可用空间检查测试了我的想法,并且效果很好(尽管我没有执行长期测试),但是 @Clifford 回答和这篇文章说服我放弃动态分配的想法。最终,我实现了我自己的静态堆,其中包含页面(2d 数组)、占用页面指示器(0-1 页面大小数组)和结构的 fifo,包括指向我的静态堆上的 msg 的指针(实际上只是索引数组)和消息长度(以确定它占用多少连续页面)。我收到的 95% 的 msg 应该只占用一页,5% - 2 或 3 页,所以碎片仍然是可能的,但至少我严格控制它,它只影响分配给这个模块的内存部分代码(换句话说:碎片不会泄漏到代码的其他部分)。到目前为止,它的工作没有任何问题,而且肯定更快,因为查找时间是 O(n*m),n - 页面数,m - 可能的最长页面,但考虑到概率法则,它下降到 O(n)。此外,n 总是比内存中所有分配单元的数量小很多,因此寻找的方式更少。

于 2015-07-01T09:03:43.947 回答