FreeRTOS中对于内存的管理当前一共有5种实现方式(作者当前的版本是8.2.1),均在【 \Source\portable\MemMang 】下面,这里笔记下。
heap_2.c:
/* |
* A sample implementation of pvPortMalloc() and vPortFree() that permits |
* allocated blocks to be freed, but does not combine adjacent free blocks |
* into a single larger block (and so will fragment memory). See heap_4.c for |
* an equivalent that does combine adjacent blocks into single larger blocks. |
* |
* See heap_1.c, heap_3.c and heap_4.c for alternative implementations, and the |
* memory management pages of http://www.FreeRTOS.org for more information. |
*/ |
/* |
一种非常简单的内存实现方式,和heap_1.c的实现差不多,内部也是一个大号的数组,不过实现了内存的回收,但是没有实现空闲内存的整合,因此 |
会产生内存碎片 |
*/ |
|
|
#include <stdlib.h> |
/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining |
all the API functions to use the MPU wrappers. That should only be done when |
task.h is included from an application file. */ |
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE |
#include "FreeRTOS.h" |
#include "task.h" |
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE |
/* A few bytes might be lost to byte aligning the heap start address. */ |
#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) |
//由于可能会出现内存对齐问题,数组起始地址可能会前移,因此实际的数组大小可能小于申请的大小,这里为了简单,直接向前移动了portBYTE_ALIGNMENT个字节。 |
/* |
* Initialises the heap structures before their first use. |
*/ |
static void prvHeapInit( void ); |
//用来申请内存的大号数组 |
/* Allocate the memory for the heap. */ |
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; |
//空闲内存管理结构体,通过它来管理释放回来的内存 |
/* Define the linked list structure. This is used to link free blocks in order |
of their size. */ |
typedef struct A_BLOCK_LINK |
{ |
struct A_BLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ |
/* 链表形式存储,保存指向下一块空闲内存的结构体,注意这里是单向链表 */ |
size_t xBlockSize; /*<< The size of the free block. */ |
/* 该块空闲内存的大小,注意这里BlockLink_t其实也是放在它对应的空余内存的头部的,但是空闲内存的大小并没有考虑放入的BlockLink_t */ |
} BlockLink_t; |
/* 考虑到字节对齐后BlockLink_t的大小 */ |
static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); |
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) |
/* BlockLink_t是以单向链表方式储存的,这里存储下链表的头和尾 */ |
/* Create a couple of list links to mark the start and end of the list. */ |
static BlockLink_t xStart, xEnd; |
/* Keeps track of the number of free bytes remaining, but says nothing about |
fragmentation. */ |
/* 当前剩余的空闲内存总大小 */ |
static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE; |
/* STATIC FUNCTIONS ARE DEFINED AS MACROS TO MINIMIZE THE FUNCTION CALL DEPTH. */ |
/* |
* Insert a block into the list of free blocks - which is ordered by size of |
* the block. Small blocks at the start of the list and large blocks at the end |
* of the list. |
*/ |
/* |
工具宏,将一个BlockLink_t的指针插入到单向链表中,单向链表以空闲内存大小为序排列,内存小的在头部 |
*/ |
|
#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; \ |
} |
/*-----------------------------------------------------------*/ |
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 ) |
{ |
/* 空余内存的头部要放一个BlockLink_t来管理,因此这里需要人为的扩充下申请的内存大小 */ |
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. */ |
//从空余内存链表的头部开始找,如果该空余内存的大小>xWantedSize,就从这块内存中抠出一部分内存返回,剩余的内存生成新的BlockLink_t插入链表中 |
|
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 ) |
{ |
/* 找到了,就把该块内存返回给用户,注意内存的头部有BlockLink_t,需要偏移掉 */ |
/* 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 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; //这里向前偏移,重新找回BlockLink_t |
/* 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 ) ); //把BlockLink_t放回链表 |
xFreeBytesRemaining += pxLink->xBlockSize; //更新剩余内存总大小 |
traceFREE( pv, pxLink->xBlockSize ); |
} |
( void ) xTaskResumeAll(); |
} |
} |
/*-----------------------------------------------------------*/ |
size_t xPortGetFreeHeapSize( void ) |
{ |
return xFreeBytesRemaining; |
} |
/*-----------------------------------------------------------*/ |
void vPortInitialiseBlocks( void ) |
{ |
/* This just exists to keep the linker quiet. */ |
} |
/*-----------------------------------------------------------*/ |
static void prvHeapInit( void ) |
{ |
BlockLink_t *pxFirstFreeBlock; |
uint8_t *pucAlignedHeap; |
//保证pucAlignedHeap也是在按照指定内存要求对齐的,通过这里可以知道,初始化pucAlignedHeap时并不是一定等于&ucHeap[0]的,而是会根据字节对齐的要求,在&ucHeap[0]和&ucHeap[portBYTE_ALIGNMENT]之间,这就会导致ucHeap的前几个字节可能会被浪费到,这也是为什么会有一个configADJUSTED_HEAP_SIZE。 |
/* Ensure the heap starts on a correctly aligned boundary. */ |
pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); |
|
/* 静态变量的初始化其实就是链表的初始化,这里将所有的空余内存作为一个BlockLink_t,放入空闲列表中 */ |
/* 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. */ |
/*初始化链表的头部 */ |
/* 个人感觉这里的代码写的不好,应该先对pxFirstFreeBlock赋值,然后将xStart.pxNextFreeBlock = pxFirstFreeBlock 就更好了 */ |
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; |
/* 将pxFirstFreeBlock 放入空闲链表中 */ |
/* 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; //注意这里的空余内存大小标记为configADJUSTED_HEAP_SIZE,并没有减去BlockLink_t的大小,因此在后面的内存申请中又人为的加了一个heapSTRUCT_SIZE。 |
pxFirstFreeBlock->pxNextFreeBlock = &xEnd; |
} |
/*-----------------------------------------------------------*/ |
发表评论