嵌入式系统中有哪些好的C内存分配器?
我有一个单线程、嵌入式应用程序,它分配和释放大量的小块(32-64B)。这是缓存分配器的完美应用场景。虽然我可以尝试编写一个,但这可能是浪费时间的,而且没有经过充分测试和调试的方案可能不如已在前线测试过的方案。
那么我应该使用什么最好的分配器?
注意:我在系统中使用了 Lua 虚拟机(这是80%以上分配的罪魁祸首),因此我不能轻易地重构我的代码以使用栈分配来提高分配性能。
原文链接 https://stackoverflow.com/questions/177209
我最近做了一些关于这个话题的研究,因为我们遇到了内存碎片化的问题。最终我们决定使用GNU libc的实现,并在必要时添加一些应用级别的内存池。还有其他的分配器具有更好的碎片化行为,但我们不太舒服使用它们来全局替换malloc。GNU的好处在于背后有着悠久的历史。
在你的情况下,假设你不能修复VM,那么这些微小的分配是非常浪费的。我不知道你的整个环境是什么,但你可以考虑对VM上malloc/realloc/free的调用进行封装,这样你就可以把它传递给设计用于小型池的处理程序。
虽然这已经过去了一段时间,但我的最终解决方案是使用 LoKi 的 SmallObjectAllocator,它的表现非常出色。去掉了所有操作系统调用,提高了嵌入式设备上我 Lua 引擎的性能。非常好而且简单,仅仅需要五分钟的工作!
我仅仅想补充一点,尽管这是一个旧的话题。在嵌入式应用中,如果您能够分析您的应用程序的内存使用情况,并得出各种大小的最大内存分配数量,通常使用内存池的分配器是最快的。在我们的嵌入式应用中,我们可以确定运行时所需的所有分配大小。如果您能够做到这一点,您可以完全消除堆碎片并且进行非常快速的分配。大多数这些实现都有一个溢出池,可以在特殊情况下进行常规的 malloc,但是如果您分析得正确,这种特殊情况应该非常少见。
我曾在vxworks下成功地使用了“二进制伙伴”系统。基本上,您通过将块对半切割来分配堆,以获得最小的2次幂大小的块来容纳您的请求,并且当块被释放时,您可以通过在树上向上遍历来将块合并在一起以缓解碎片化。您可以通过谷歌搜索找到所有您需要的信息。
我有点晚来到这个场合,但我想分享一下我最近发现并测试的非常高效的嵌入式系统内存分配器:https://github.com/dimonomid/umm_malloc
这是一个专门设计用于ARM7的内存管理库,我个人在PIC32设备上使用它,但它应该适用于任何16位和8位设备(我计划在16位PIC24上测试它,但我还没有测试过)。
默认分配器在碎片化方面严重受挫:我的项目经常分配各种大小的块,从几个字节到几百个字节不等,有时我会遇到'内存不足'错误。我的PIC32设备总共有32K的RAM,其中8192字节用于堆。在特定时刻,有超过5K的空闲内存,但默认分配器最大的非碎片化内存块仅约为700字节,因为碎片。这太糟糕了,所以我决定寻找更有效的解决方案。
我已经意识到了一些分配器,但它们都有一些限制(例如块大小应是2的幂,而且起始大小不是2而是例如128字节),或者只是有缺陷。以前每次都要切换回默认的分配器。
但这一次,我很幸运:我找到了这个:http://hempeldesigngroup.com/embedded/stories/memorymanager/
当我尝试这个内存分配器时,在完全相同的有5K的空闲内存的情况下,它有超过3800字节的块!这对我来说是如此难以置信(与700字节相比),我进行了严格的测试:设备工作了超过30个小时。没有内存泄漏,一切正常运行。
我也在FreeRTOS存储库中找到了这个分配器:http://svnmios.midibox.org/listing.php?repname=svn.mios32&path=%2Ftrunk%2FFreeRTOS%2FSource%2Fportable%2FMemMang%2F&rev=1041&peg=1041#,这事实是umm_malloc稳定性的额外证据。
所以我完全切换到umm_malloc,并对此感到非常满意。
我只需要稍微更改一下:当未定义宏UMM_TEST_MAIN时,配置有点有问题,因此,我创建了github存储库(链接在本帖子的顶部)。现在,用户依赖配置存储在单独的文件umm_malloc_cfg.h中。
我还没有深入研究这个分配器中应用的算法,但它具有非常详细的算法说明,因此任何感兴趣的人都可以查看umm_malloc.c文件的顶部。至少,“binning”方法应该在减少碎片方面提供巨大的好处:http://g.oswego.edu/dl/html/malloc.html
我相信任何需要高效内存分配器的微控制器的人,应该至少尝试一下这个。
我正在编写一个称为tinymem的C内存分配器,旨在能够整理堆,并重新使用内存。看看它:
https://github.com/vitiral/tinymem
注意:这个项目已经停用,因为我在开发Rust的实现:
https://github.com/vitiral/defrag-rs
此外,我之前没听说过umm_malloc。不幸的是,它似乎无法处理碎片,但它肯定很有用。我需要去了解一下。
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
- 如何创建一个 lua 脚本以针对特定键为 fluentbit 进行限流
在我之前处理的 C 语言项目中,为了在多种平台,包括嵌入式系统上运行库,我们尝试实现了自己的内存管理例程。该库还分配和释放了大量的小缓冲区。它运行得相对稳定,实现起来也并不需要大量的代码。如果您打算自己开发某些东西,我可以给您提供一些实现背景。
基本实现包含了一组管理一定大小缓冲区的例程。这些例程作为 malloc() 和 free() 的包装器使用。我们使用这些例程来管理我们经常使用的结构的分配,并且还用它们来管理确定大小的通用缓冲区。我们使用结构来描述每种被管理的缓冲区类型。当分配特定类型的缓冲区时,如果空闲缓冲区列表为空,我们会在块(block)中使用 malloc() 来分配内存。例如,如果我们管理 10 字节的缓冲区,我们可能会对其中一个 malloc() 分配空间以容纳 100 个这样的缓冲区,以减少碎片化和底层 malloc 需求的数量。
每个缓冲区的前面都有一个指针,用于在一个空闲缓冲区列表中进行链式管理缓冲区。当分配 100 个缓冲区时,每个缓冲区都会在空闲列表中链接在一起。当缓冲区正在使用时,该指针将被设置为空。我们还维护了缓冲区块的列表,以便我们可以通过在每个实际 malloc 的缓冲区上调用 free() 进行简单的清理。
为了管理动态缓冲区大小,我们还在每个缓冲区的开头添加了一个 size_t 变量,用于告诉缓冲区的大小。然后将其用于确定在释放缓冲区时将缓冲区放回哪个缓冲区块。我们为 malloc() 和 free() 设计了替代例程,通过指针算术来获取缓冲区大小,然后将缓冲区放入空闲列表中。我们还对我们管理的缓冲区大小设置了限制。超过这个限制的缓冲区将被简单地分配,然后交给用户使用。对于我们管理的结构,我们创建了包装器例程,用于分配和释放特定的结构。
最终,当用户要求清理未使用的内存时,我们还将使用垃圾回收系统演变到包含垃圾回收。由于我们对整个系统具有控制权,因此我们能够随着时间的推移进行各种优化,以提高系统性能。正如我所提到的,它确实起到了不错的作用。