提问人:Robert Gould 提问时间:10/7/2008 更新时间:10/11/2016 访问量:8127
什么是好的嵌入式系统 C 内存分配器?[关闭]
What's a good C memory allocator for embedded systems? [closed]
问:
我们不允许提出有关书籍、工具、软件库等建议的问题。您可以编辑问题,以便用事实和引文来回答。
7年前关闭。
我有一个单线程的嵌入式应用程序,可以分配和释放很多很多的小块(32-64b)。基于缓存的分配器的完美方案。虽然我可以尝试写一个,但它可能会浪费时间,而且不像一些已经在前线的解决方案那样经过很好的测试和调整。
那么,在这种情况下,我可以使用的最佳分配器是什么?
注意:我在系统中使用了 Lua 虚拟机(这是 80+% 分配的罪魁祸首),所以我不能轻易重构我的代码以使用堆栈分配来提高分配性能。
答:
在我过去参与的一个 C 语言项目中,我们走上了为运行在各种平台(包括嵌入式系统)上的库实现我们自己的内存管理例程的道路。该库还分配并释放了大量小缓冲区。它运行得相对较好,不需要大量的代码来实现。我可以给你一些关于这个实现的背景,以防你想自己开发一些东西。
基本实现包括一组例程,用于管理设定大小的缓冲区。这些例程被用作 malloc() 和 free() 的包装器。我们使用这些例程来管理我们经常使用的结构的分配,以及管理设置大小的通用缓冲区。使用一种结构来描述所管理的每种类型的缓冲区。当分配了特定类型的缓冲区时,我们将以块为单位对内存进行 malloc() 处理(如果可用缓冲区列表为空)。IE,如果我们要管理 10 字节缓冲区,我们可能会创建一个包含 100 个缓冲区空间的 malloc(),以减少碎片和所需的底层 malloc 数量。
在每个缓冲区的前面都有一个指针,用于将缓冲区链接到一个空闲列表中。分配 100 个缓冲区后,每个缓冲区将在空闲列表中链接在一起。使用缓冲区时,指针将设置为 null。我们还维护了一个缓冲区“块”列表,以便我们可以通过在每个实际的 malloc 缓冲区上调用 free() 来进行简单的清理。
为了管理动态缓冲区大小,我们还在每个缓冲区的开头添加了一个size_t变量,用于告知缓冲区的大小。然后,这用于确定在释放缓冲区时将缓冲区放回哪个缓冲区块。我们有 malloc() 和 free() 的替换例程,它们执行指针算术以获取缓冲区大小,然后将缓冲区放入可用列表中。我们对管理的缓冲区大小也有限制。大于此限制的缓冲区被简单地恶意处理并传递给用户。对于我们管理的结构,我们创建了用于分配和释放特定结构的包装例程。
最终,我们还改进了系统,在用户请求清理未使用的内存时包括垃圾回收。由于我们可以控制整个系统,因此随着时间的推移,我们能够进行各种优化以提高系统的性能。正如我所提到的,它确实运行良好。
我最近对这个话题做了一些研究,因为我们遇到了内存碎片的问题。最后,我们决定继续使用 GNU libc 的实现,并在必要时添加一些应用程序级内存池。还有其他分配器具有更好的碎片行为,但我们对它们全局替换 malloc 还不够满意。GNU背后有悠久的历史。
就你而言,这似乎是有道理的;假设您无法修复 VM,那么这些微小的分配是非常浪费的。我不知道你的整个环境是什么,但你可以考虑只在VM上包装对malloc/realloc/free的调用,以便你可以将其传递给为小型池设计的处理程序。
评论
虽然我问这个问题已经有一段时间了,但我最终的解决方案是使用 LoKi 的 SmallObjectAllocator,它效果很好。摆脱了所有的操作系统调用,并提高了嵌入式设备的 Lua 引擎的性能。非常好,很简单,只需要大约5分钟的工作!
从 5.1 版本开始,Lua 允许在创建新状态时设置自定义分配器。
我还想补充一点,即使它是一个旧线程。在嵌入式应用程序中,如果可以分析应用程序的内存使用情况,并提出不同大小的最大内存分配数,则通常最快的分配器类型是使用内存池的分配器。在我们的嵌入式应用程序中,我们可以确定运行时所需的所有分配大小。如果可以做到这一点,则可以完全消除堆碎片,并实现非常快速的分配。这些实现中的大多数都有一个溢出池,它将对特殊情况进行常规的 malloc,如果您分析得当,希望两者之间会相距甚远。
评论
我在 vxworks 下使用了“二进制伙伴”系统,效果很好。基本上,您可以通过将块切成两半来分配堆,以获得两个大小块的最小幂来容纳您的请求,当块被释放时,您可以向树上传,将块合并在一起以减轻碎片。谷歌搜索应该会出现您需要的所有信息。
我来晚了一点,但我只想分享我最近发现和测试的嵌入式系统的非常高效的内存分配器:https://github.com/dimonomid/umm_malloc
这是一个专门设计用于 ARM7 的内存管理库,我个人在 PIC32 设备上使用它,但它应该适用于任何 16 位和 8 位设备(我计划在 16 位 PIC24 上进行测试,但我还没有测试它)
我被默认分配器的碎片严重殴打:我的项目经常分配各种大小的块,从几个字节到几百个字节不等,有时我会遇到“内存不足”错误。我的 PIC32 设备总共有 32K 的 RAM,8192 字节用于堆。在特定时刻,有超过 5K 的可用内存,但由于碎片,默认分配器的最大非碎片内存块仅为 700 字节左右。这太糟糕了,所以我决定寻找更有效的解决方案。
我已经知道一些分配器,但它们都有一些限制(例如块大小应该是 2 的幂或 2,并且不是从 2 开始,而是从 128 字节开始),或者只是有问题。以前每次,我都必须切换回默认分配器。
但这一次,我很幸运:我找到了这个:http://hempeldesigngroup.com/embedded/stories/memorymanager/
When I tried this memory allocator, in exactly the same situation with 5K of free memory, it has more than 3800 bytes block! It was so unbelievable to me (comparing to 700 bytes), and I performed hard test: device worked heavily more than 30 hours. No memory leaks, everything works as it should work. I also found this allocator in the FreeRTOS repository: http://svnmios.midibox.org/listing.php?repname=svn.mios32&path=%2Ftrunk%2FFreeRTOS%2FSource%2Fportable%2FMemMang%2F&rev=1041&peg=1041# , and this fact is an additional evidence of stability of umm_malloc. So I completely switched to umm_malloc, and I'm quite happy with it.
I just had to change it a bit: configuration was a bit buggy when macro UMM_TEST_MAIN is not defined, so, I've created the github repository (the link is at the top of this post). Now, user dependent configuration is stored in separate file umm_malloc_cfg.h
I haven't got deeply yet in the algorithms applied in this allocator, but it has very detailed explanation of algorithms, so anyone who is interested can look at the top of the file umm_malloc.c . At least, "binning" approach should give huge benefit in less-fragmentation: http://g.oswego.edu/dl/html/malloc.html
I believe that anyone who needs for efficient memory allocator for microcontrollers, should at least try this one.
评论
I am writing a C memory allocator called tinymem that is intended to be able to defragment the heap, and re-use memory. Check it out:
https://github.com/vitiral/tinymem
Note: this project has been discontinued to work on the rust implementation:
https://github.com/vitiral/defrag-rs
Also, I had not heard of umm_malloc before. Unfortunately, it doesn't seem to be able to deal with fragmentation, but it definitely looks useful. I will have to check it out.
评论