概述
简要介绍
heap2采用链表的方法管理内存堆。链表结构体的next指针指向下一个空闲内存块。并且链表连接的内存块按照从小到大的顺序排列。分配内存的时候,可以遍历查询。
申请内存时,查询到合适大小的内存后,如果内存有多,那么需要割下多余内存,重新插入到内存链表中,按照从小到大的顺序,在合适的位置插入。释放内存的时候,通过释放的地址找到该内存块的链表结构体,给出要释放的内存的大小,并插入到内存堆链表中,同样依照从小到大的顺序。
内存块分配预备内容
heap堆大小heap[]
在heap_2.c 中定义 static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
内存堆为ucHeap[],大小为 configTOTAL_HEAP_SIZE,由宏定义 #define configTOTAL_HEAP_SIZE ((size_t) (40 * 1024)),可以得到内存堆大小为40K字节。
字节对齐
"portmacro.h"
#define portBYTE_ALIGNMENT 8
"portable.h"
#if portBYTE_ALIGNMENT == 8
#define portBYTE_ALIGNMENT_MASK ( 0x0007U )
#endif
void *pvPortMalloc( size_t xWantedSize ){
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
static void prvHeapInit( void ){
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) );
}
宏 portBYTE_ALIGNMENT 是需要对齐的字节数,默认为8,需进行8字节对齐也就是说xWantedSize 要为8的倍数,若不是就需要调整为8的倍数。如下:
xWantedSize += ( 8 - (xWantedSize & 0x07) )
eg1. xWantedSize = 30;
xWantedSize = 30 + (8 - (30 & 0x07))= 30 + (8 - 6)= 32
所以需要申请的内存大小应该为32个字节
pucAlignedHeap = ( uint8_t * )( ( uint32_t ) &ucHeap[8] ) & ( ( uint32_t ) ~0x07)
eg2. pucAlignedHeap = 0x200006C4;
pucAlignedHeap = 0x200006CC & 0xFFFFFFFFF8 = 0x200006C8
多以内存块的首地址应该是0x200006C8
内存块结构体BlockLink_t
typedef struct A_BLOCK_LINK
{
struct A_BLOCK_LINK *pxNextFreeBlock;
size_t xBlockSize;
} BlockLink_t;
为了实现内存释放,heap_2引入了内存块概念,每分出一段内存就是一个内存块,剩下的空闲内存也是一个内存块,内存块大小不定。
每个内存块前面都会有一个BlockLink_t类型的变量来描述此内存块,比如现在申请一个16个字节的内存块。那么就需要申请24个字节的内存块,16字节给申请的内存,另外8个字节来保存BlockLink_t类型结构体变量,xBlockSize记录的是整个内存块的大小。
内存管理函数详解
内存堆初始化函数
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
/* Ensure the heap starts on a correctly aligned boundary. */
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ( portPOINTER_SIZE_TYPE ) ~portBYTE_ALIGNMENT_MASK ) );
/* xStart is used to hold a pointer to the first item in the list of free
blocks. The void cast is used to prevent compiler warnings. */
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
/* xEnd is used to mark the end of the list of free blocks. */
xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
xEnd.pxNextFreeBlock = NULL;
/* To start with there is a single free block that is sized to take up the
entire heap space. */
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
pxFirstFreeBlock->pxNextFreeBlock = &xEnd;
}
prvHeapInit()
- pucAlignedHeap 内存堆的初始地址,进行八字节地址对齐。
- XStart的next指针指向内存堆首地址,大小设为0.
- XEnd大小设为内存堆总大小configADJUSTED_HEAP_SIZE,next指针指向NULL。(在内存申请的判断中有用到)
- 初始化第一块内存pxFirstFreeBlock,地址为内存堆首地址,大小为内存堆总大小,next指针指向Xend。
内存块插入函数
#define prvInsertBlockIntoFreeList( pxBlockToInsert )
{
BlockLink_t *pxIterator;
size_t xBlockSize;
xBlockSize = pxBlockToInsert->xBlockSize;
/* Iterate through the list until a block is found that has a larger size */
/* than the block we are inserting. */
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock )
{
/* There is nothing to do here - just iterate to the correct position. */
}
/* Update the list to include the block being inserted in the correct */
/* position. */
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
prvInsertBlockIntoFreeList( pxBlockToInsert )宏定义
- 给出要插入的内存块的地址 pxBlockToInsert
- 在链表结构体内获取插入的内存块的大小xBlockSize,
- 从XStart开始不断遍历查找下一个空闲内存块,知道有内存块比pxBlockToInsert 大。当前内存块大小pxIterator->pxNextFreeBlock->xBlockSize 大于 xBlockSize
- 将Insert结构体的next指针指向pxIterator->pxNextFreeBlock , 而pxIterator的next指向插入的Insert结构体。因此Insert就插入到了链表中。
内存申请函数
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
static BaseType_t xHeapHasBeenInitialised = pdFALSE;
void *pvReturn = NULL;
vTaskSuspendAll();
{
/* If this is the first call to malloc then the heap will require
initialisation to setup the list of free blocks. */
if( xHeapHasBeenInitialised == pdFALSE )
{
prvHeapInit();
xHeapHasBeenInitialised = pdTRUE;
}
/* The wanted size is increased so it can contain a BlockLink_t
structure in addition to the requested amount of bytes. */
if( xWantedSize > 0 )
{
xWantedSize += heapSTRUCT_SIZE;
/* Ensure that blocks are always aligned to the required number of bytes. */
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 )
{
/* Byte alignment required. */
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
}
if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) )
{
/* Blocks are stored in byte order - traverse the list from the start
(smallest) block until one of adequate size is found. */
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
/* If we found the end marker then a block of adequate size was not found. */
if( pxBlock != &xEnd )
{
/* Return the memory space - jumping over the BlockLink_t structure
at its start. */
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE );
/* This block is being returned for use so must be taken out of the
list of free blocks. */
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
/* If the block is larger than required it can be split into two. */
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
/* This block is to be split into two. Create a new block
following the number of bytes requested. The void cast is
used to prevent byte alignment warnings from the compiler. */
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
/* Calculate the sizes of two blocks split from the single
block. */
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
pxBlock->xBlockSize = xWantedSize;
/* Insert the new block into the list of free blocks. */
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
xFreeBytesRemaining -= pxBlock->xBlockSize;
}
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
}
#endif
return pvReturn;
}
void *pvPortMalloc( size_t xWantedSize )函数
- 判断内存堆是否初始化,若没有则调用prvHeapInit()进行初始化操作。
- 判断申请的内存xWantedSize大于0,将xWantedSize加上链表结构体的大小heapSTRUCT_SIZE。把xWantedSize进行8字节对齐。同时heapSTRUCT_SIZE也同样进行过八字节对齐。
- 从xStart结构体开始遍历,找到大小匹配的内存块pxBlock,得到pxPreviousBlock和pxBlock的地址。
- 判断pxBlock是否是XEnd的地址,如果是,那么表明没找到所需大小的内存块,可能是内存块太大,或者内存块用完。
- 给出返回指针pvReturn,找到的内存块,需要加上链表结构体大小heapSTRUCT_SIZE,过滤掉链表。(申请内存后链表不被操作,等下次回收内存后,依然保有内存块链表,能知道内存块的大小)
- 上一个内存块的next指针指向block的next内存块。把pxBlock移除内存块链表。
- 如果得到的内存块要比所需内存来的大,需要切割内存块,把剩余的内存大小插入到空闲内存块链表中。
- xFreeBytesRemaining 减去被取出内内存块大小,调用xPortGetFreeHeapSize()函数能获得xFreeBytesRemaining 的值,从而知晓剩余空闲内存块的大小。
内存块释放函数
void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
/* The memory being freed will have an BlockLink_t structure immediately
before it. */
puc -= heapSTRUCT_SIZE;
/* This unexpected casting is to keep some compilers from issuing
byte alignment warnings. */
pxLink = ( void * ) puc;
vTaskSuspendAll();
{
/* Add this block to the list of free blocks. */
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
}
( void ) xTaskResumeAll();
}
}
vPortFree()函数
- 获得索要释放的内存块首地址 uint8_t *puc = ( uint8_t * ) pv;
- 内存块首地址puc 减去链表结构体大小heapSTRUCT_SIZE,就是该内存块对应的链表结构体的地址。其中包括next指针和内存块大小。
- 调用内存块插入函数prvInsertBlockIntoFreeList()把释放的内存块插入到内存块链表中去。
- 剩余空闲内存大小xFreeBytesRemaining 加。
FreeRTOS内存管理heap2与QP事件池的内存管理比较
QP
- qp事件池管理中,在链表中内存块的大小固定不变的,并在链表头申请或释放内存,并且链表头的内存块作为当前空闲内存块,以便下一次操作。
- 链表结构体QFreeBlock中只有一个元素next指针,指向下一个空闲块,申请内存后,得到的内存块,包括链表结构体所在位置均可使用。放内存块时,将该段内存强制转化为QFreeBlock结构体类型。存放next指针,指向之前的free当前空闲块。
FreeRTOS
.
- FreeRtos中采用遍历的方法,由从小到大的顺序依次排列,内存块大小不一致,在插入内存块的时候要依照顺序。
- 链表结构体BlockLink_t包含next指针和内存块大小。使用内存块的时候皆跳过链表结构体所在地址,当释放内存的时候可以由链表结构体知道要释放多大的内存。
heap2的内存堆大小为configTOTAL_HEAP_SIZE
最后
以上就是冷酷茉莉为你收集整理的FreeRTOS内存管理heap_2的全部内容,希望文章能够帮你解决FreeRTOS内存管理heap_2所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复