完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛|
之前看了一下原子哥的内存管理程序,随着内存池的增大,内存管理表也会增大,会浪费一点内存空间,觉得不爽。一直想自己写一个,又不想重复造轮子,最近比较闲,研究了一下FreeRTOS的内存管理Heap_4.c,发现它的实现方法还挺简单的,而且比较实用,不过为了要像原子哥的内存管理那样管理三个内存池,还需要稍稍修改一下,我已经修改好了,测试了一下没发现问题,上传上来给大家瞧瞧,有兴趣的可以帮忙测试一下,哈哈。
既然是在FreeRTOS内存管理Heap_4.c的基础上稍稍修改的,那还是先介绍一下它的实现方法吧: 以下为转载内容,原文链接:http://xilinx.eetrend.com/blog/8197: FreeRTOS8.0.1内存管理的最后一个堆模型Heap_4,貌似是在这一个版本才有的。所以找到的说明几乎没有。代码的开头注释也只是简单地说了一下实现了pvPortMalloc()和vPortFree()两个函数,并且能够对回收的内存块进行合并,减少碎片的出现。(A sample implementation of pvPortMalloc() and vPortFree() that combines (coalescences) adjacent memory blocks as they are freed, and in so doing limits memory fragmentation.)不过经过这一次的剖析之后,发现Heap_4所用的内存管理算法为首次适配法(first fit algorithm)。 和Heap_2一样,Heap_4先申请了一个数组ucHeap[ configTOTAL_HEAP_SIZE ]作为自己管理的堆空间,同样也有空闲块头结构BlockLink_t来管理空闲块。但是和Heap_2不一样的是,Heap_4用了BlockLink_t中xBlockSize的最高一位来标识某个内存块是否处于空闲状态。所以这就是为什么会有一个宏heapBITS_PER_BYTE的出现,而且定义为( ( size_t ) 8 )。这样一来,每一个分配出去的内存块大小就有限制了。例如,我用的是STM32F103,size_t是定义为unsigned int类型的,32位,可支持到4G的内存空间。但是最高1位用来指示空间状态的话,那就只有31位去标识内存块地址,即只支持到2G的内存空间。所以用Heap_4还是有一点点代价的,特别是用在16位或8位的单片机上。 还是先剖析一下堆空间的初始化过程prvHeapInit()。首先还是先将内存堆进行首地址对齐。接下来就是运用xStart和pxEnd来组织整个空闲块链表。要注意的是,xStart是BlockLink_t的一个实体变量,存储在静态存储区,而pxEnd只是BlockLink_t的一个指针,存储在静态存储区中,却指向了内存堆的最后一个BlockLink_t大小的位置上。也就是说,内存堆最后的空间是存储着一个BlockLink_t,用来指示空闲块链表的终结,这是和Heap_2有所不同的地方。下图说明了初始化流程最终将空闲块链表组织成的样子。 以上的预处理完成了,开始进入分配算法的核心了。只要最终申请的空间大小仍在空闲空间大小的范围内,则进入内存的分配。首先遍历链表,找到第1块能比申请空间大小大的空闲块,修改空闲块的信息,记录用户可用的内存首地址。接下来,如果分配出去的空闲块比申请的空间大很多,则将该空闲块进行分割,把剩余的部分重新添加到链表中。 分配内存的主要流程基本结束了,和之前分析的一样,pvPortMalloc()继续调用调试宏traceMALLOC()输出调试信息,恢复所有挂起的任务,并按设置调用勾子函数vApplicationMallocFailedHook(),最终把用户可用的内存首地址返回。到这里整个pvPortMalloc()就结束了。 但是,有一个地方刚刚没怎么详细讲,就是把分割出来的空闲块重新添加到链表中的过程。现在来详细分析一下,这也是Heap_4的一个重点。和Heap_2不同,这一次的prvInsertBlockIntoFreeList()并不是写成一个宏,而是写成了一个函数。进入函数的开始,可以看到,FreeRTOS实际上是将这个空闲块链表里的所有空闲块按地址顺序排列的。当然,如果不这么排列,怎么能将相邻的空闲块进行合并呢?将要回收的空闲块为pxBlockToInsert,这个空闲块将***到pxIterator的后面。通过一次链表的遍历,就把pxIterator找出来了。接下来,FreeRTOS先试着将pxIterator和pxBlockToInsert进行合并,可以合并的标准为pxIterator的首地址加上pxIterator的块大小之后等于pxBlockToInsert的首地址。相等就说明两个块是相邻的。如果不能合并,就什么事都不做。然后,FreeRTOS再试着将pxBlockToInsert和pxIterator指向的下一个空闲块进行合并。可合并的标准和刚刚说的一样,只是这次用pxBlockToInsert的首地址加上pxBlockToInsert的块大小与pxIterator指向的下一个块地址比较。能合并是最好的,不能合并,则要修改pxBlockToInsert的Next指针,指向pxIterator的下一个空闲块。这是链表插入的基本操作,不用再细讲了。最后,要是pxBlockToInsert没有和pxIterator合并,则还要修改pxIterator的Next指针,这样整条链表才完整无误。 最后一个重点是vPortFree()。不过这里的vPortFree()的流程和Heap_2的差不多,只是判断指针合法性的时候多了两个条件,一个是检查回收的块大小最高位是否为1,为1才是合法的,毕竟是分配出去了嘛。第二个是Next指针是否为空,为空了说明那是pxEnd,那就不能回收了。在这两个判断之前也有这两个条件的断言configASSERT(),定义在FreeRTOS.h里,同样也是定义为空,可能是留给用户另外用的吧。 Heap_4的其它三个函数,一个名字看上去是做什么初始化的,却什么都没有实现,所以没啥好讲的,另外两个只是用来返回内存堆的一些状态而已,所以也没啥好讲的。到这里,整个Heap_4就剖析完成了。 PS:以下是使用方法 1、在.c里注释掉#include "../BSP/sys.h",加上自己的声明OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()的头文件(其实就是进入和退出临界区,跟原子哥的一样也可以) 2、在.h里配置一下是否使用内部内存池、外部内存池、内部CCM内存池,COUT_OF_MEM是内存池数量 #define EN_SRAM_IN (1) #define EN_SRAM_EX (1) #define EN_SRAM_CCM (1) #define COUT_OF_MEM (3) //有多少块内存 3、配置各个内存池的大小 #define configTOTAL_HEAP_SIZE1 ( ( size_t ) ( 10 * 1024 ) )//内部内存池大小 #define configTOTAL_HEAP_SIZE2 ( ( size_t ) ( 900 * 1024 ) )//外部内存池大小 #define configTOTAL_HEAP_SIZE3 ( ( size_t ) ( 60 * 1024 ) )//CCM内存池大小 4、在.c里面我已经把外部内存池和CCM内存池的起始地址固定了,有需要的可以改变 #if (EN_SRAM_IN == 1) __align(8) static uint8_t ucMemSRAMIn[ configTOTAL_HEAP_SIZE1 ]; #endif #if (EN_SRAM_EX == 1) __align(8) static uint8_t ucMemSRAMEx[ configTOTAL_HEAP_SIZE2 ] __attribute__((at(0X68000000))); #endif #if (EN_SRAM_CCM == 1) __align(8) static uint8_t ucMemSRAMCCM[ configTOTAL_HEAP_SIZE3 ] __attribute__((at(0X10000000))); #endif 5、使用示例 申请外部内存:p= fMalloc(2048*1024,SRAM_EX); 释放:fFree(p,SRAM_EX); fMalloc.c (16.36 KB ) fMalloc.h (2.93 KB ) |
|
相关推荐
23个回答
|
|
|
在使用heap4的时候,一直在报一个错误,不知道是哪里的原因
Error:..FreeRTOSportableMemMangheap_4.c,320 Error:..FreeRTOSportableMemMangheap_4.c,321 实际上我使用 BT_Init_pntr = pvPortMalloc(20); 测试发现 指针也不为空,就是说分配完成了,经测试,发现报的错误是从Malloc里面的断言发出的 |
|
|
|
|
|
我也遇到这个问题,不知道您后面是怎么解决的呢?
|
|
|
|
|
|
我也遇到了这个问题,我的内存是申请过的
[C] 纯文本查看 复制代码 t = pvPortMalloc(256); printf("point(t) = %prn", t); sprintf(t, "%s:%s", serverhost, serverport); // Cache Host content = pvPortMalloc(256); sprintf(content, GET, p, t); printf("%srn", content); esp8266_send_data((u8*)content, (u8*)"200 OK", 400); // printf("%srn", USART3_RX_BUF); t = strstr((char*)USART3_RX_BUF, "{"); switchUtf8ToGbk(t, content); vPortFree(t); t = NULL; |
|
|
|
|
|
有时间学习一下
|
|
|
|
|
只有小组成员才能发言,加入小组>>
1058 浏览 1 评论
1881 浏览 0 评论
1859 浏览 1 评论
3283 浏览 5 评论
3610 浏览 9 评论
1061浏览 1评论
1884浏览 1评论
如何知道嵌入式电子控制单元 (ECU) 中的RAM使用情况?
1389浏览 1评论
1883浏览 0评论
1204浏览 0评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-10 20:28 , Processed in 0.841882 second(s), Total 79, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
6631