在学习FreeRtos的内存管理过程中,发现了一个有趣的现象。通过调用vPortFree接口释放一个外部缓冲区,内存管理也会把外部缓冲区挂载到内存管理的链表上,之后通过pvPortMalloc接口,可以从外部缓冲区上申请和释放空间。外部缓冲区释放到内存管理的链表上,有以下两种情况(读者技术水平有限,不确定是否有其他情况):
一、外部缓冲区的地址小于内存管理的堆地址
此时通过vPortFree接口释放外部缓冲区,外部缓冲区会被挂载到链表最前面(链表头xStart指向的下一个地址)。

修改的代码如下,外部缓冲区申明,堆空间地址设置为决定地址,保证外部缓冲区的地址小于堆空间地址:
static uint8_t sbuf1[108]; // 定义的外部缓冲区1
static uint8_t sbuf2[208]; // 定义的外部缓冲区2
// 内存管理的堆空间,使用绝对地址保证外部缓冲区地址在堆空间前面
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] @0x20002800;
测试的函数接口,在任务中调用:
// 内存管理测试函数,在任务中调用进行测试
void heap_test(void)
{
static uint8_t* buf1, *buf2;
BlockLink_t *pxLink;
buf1 = pvPortMalloc(10);
pxLink = (BlockLink_t *)sbuf1;
pxLink->pxNextFreeBlock = NULL;
pxLink->xBlockSize = xBlockAllocatedBit|100;
vPortFree(&sbuf1[8]); // 释放外部缓冲区sbuf1到内存管理的链表上
pxLink = (BlockLink_t *)sbuf2;
pxLink->pxNextFreeBlock = NULL;
pxLink->xBlockSize = xBlockAllocatedBit|200;
vPortFree(&sbuf2[8]); // 释放外部缓冲区sbuf2到内存管理的链表上
buf2 = pvPortMalloc(10); // 申请空间,由于sbuf2挂载在链表最前面,
// 申请的空间是sbuf2的空间
vPortFree(buf2);
vPortFree(buf1);
}
以下是外部缓冲区释放(调用vPortFree接口)到内存管理,和申请内存的过程(调用pvPortMalloc接口)。
① 外部缓冲区释放到内存管理链表前:

② 外部缓冲区1释放到内存管理链表:

③ 外部缓冲区2释放到内存管理链表:

④ 内存管理申请到外部缓冲区的空间:

⑤ 申请到的外部缓冲区空间重新释放:

二、外部缓冲区的地址大于内存管理的堆地址
修改的代码如下,外部缓冲区申明,堆空间地址设置为决定地址,保证外部缓冲区的地址大于堆空间地址:
static uint8_t sbuf1[108]; // 定义的外部缓冲区1
static uint8_t sbuf2[208]; // 定义的外部缓冲区2
// 内存管理的堆空间,使用绝对地址保证外部缓冲区地址在堆空间后面
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] @0x20000000;
把外部缓冲区释放到内存管理的链表时,需要获取释放的内存块需要插入的链表位置(链表结点排序规则是根据指向的内存块地址由小到大排序),因为外部缓冲区地址大于堆空间地址,所有会遍历到链表的尾部,链表的尾部pxEnd的下一个内存块地址指针指向NULL,会导致不可控的溢出发生。
本文探讨了FreeRTOS中外部缓冲区如何通过vPortFree接口加入内存管理链表,并通过pvPortMalloc申请空间。讨论了不同地址情况下外部缓冲区的插入位置及可能的问题。
2万+

被折叠的 条评论
为什么被折叠?



