我目前正在使用STM32CubeMx和Keil uVision开发STM32F407的应用程序。我知道嵌入式系统中的动态内存分配主要是不鼓励的,但是从互联网上的点到点我可以找到一些有利于它的论据。
由于我的发明者灵魂,我想尝试这样做,但安全地做到了。让我们假设我为传入的UART消息创建一个动态分配的fifo,保存由msg本身及其组成的结构。长度。但是我不想消耗所有堆大小,所以我想检查一下我剩下多少:我新(?)想法是暂时尝试分配一大块内存(比如100个字符) - 如果它成功了,我接受传入的消息,如果没有 - 这意味着我已经用尽了堆并忽略了msg (或接受它并将最旧的队列出列)。检查后我当然释放了临时记忆。
我脑子里出现了一些问题:
可能对于你们中的一些人来说,安全的动态记忆"和"嵌入"在一个帖子中既有刺激又令人眼花缭乱,但请记住,这是在尝试和探索新的视野:)谢谢和问候。
答案 0 :(得分:1)
Keil uVision仅描述了IDE。如果您正在使用意味着ARM的RealView编译器的KEil MDK-ARM,那么您可以使用__heapstats()
function获得准确的堆信息。
__heapstats()
有点奇怪,它不是简单地返回一个值,而是将堆信息输出到由传递给它的函数指针和文件描述符促成的格式化输出流。输出函数必须具有fprintf()
类似的接口。您当然可以使用fprintf()
,但这需要您正确retargetted the 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不会在默认链接描述文件中执行此操作,而是分配固定大小的堆。
您可以使用链接器映射文件摘要来确定未使用的空间并手动扩展堆。我通常会这样做,当静态分配的数据量可能增加时,留下少量未使用的空间来考虑维护。但是在某些时候;你最终耗尽了内存,来自链接器的神秘错误消息可能不会让你的堆太大了。可以覆盖默认的链接器脚本并提供您自己的脚本,并且毫无疑问可以自动调整堆的大小 - 尽管我从未尝试过尝试它。
答案 1 :(得分:1)
好吧,我已经通过动态堆空闲空间检查测试了我的想法并且它运行良好(虽然我没有执行长期测试),但@Clifford回答和this article说服我放弃了动态的想法分配。最终我实现了我自己的静态堆,页面(2d数组),占用页面指示符(0-1数组大小的数组)和fifo结构组成的指向我的静态堆上的msg(实际上只是索引)数组)和消息长度(确定它占用多少个连续页面)。我收到的msg的95%应该只占用一页,5% - 2或3页,所以仍然可以分段,但至少我会严格控制它,它只会影响分配给这个模块的内存部分。代码(换句话说:碎片不会泄漏到代码的其他部分)。到目前为止,它没有任何问题,并且确实更快,因为查找时间是O(n * m),n - 页数,m - 可能的最长页面,但考虑到概率的规律,它归结为上)。此外,n总是比内存中所有分配单元的数量小很多,因此寻找的方式要少得多。