1. 程式人生 > >FreeRTOS程式碼閱讀筆記:heap_1.c

FreeRTOS程式碼閱讀筆記:heap_1.c

FreeRTOS中對於記憶體的管理當前一共有5種實現方式(作者當前的版本是10.1.1),均在【 \Source\portable\MemMang 】下面,這裡筆記下。

重要的引數: 

使用方法:
標頭檔案:FreeRTOSConfig.h 
配置引數:
configTOTAL_HEAP_SIZE             定義系統所用的堆疊大小。
configUSE_MALLOC_FAILED_HOOK      預設0: 1則開啟鉤子函式,記憶體分配失敗則呼叫 

函式呼叫:	
      vPortInitialiseBlocks();//初始化
      ptr=pvPortMalloc(1024);
	  if(ptr !=NULL)
	  {
	  freemem=xPortGetFreeHeapSize(); 
	  printf("剩餘記憶體 %d \r\n",i,freemem); 
	  }
	  else
	  {
	  printf("獲取記憶體失敗\r\n");break;
	  }

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

heap_1.c分析:

/**************************************
重要的引數備註:
(1)FreeRTOS  記憶體堆為:ucHeap[] 大小為 configTOTAL_HEAP_SIZE 

(2)pucAlignedHeap 作為堆疊位元組對齊後的起始地址(怎麼實現的思考一下)
   pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ])& ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );

(3)configADJUSTED_HEAP_SIZE      堆疊可操作空間,減去對齊單位,防止越界
#define configADJUSTED_HEAP_SIZE	( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) 

保證pucAlignedHeap也是在按照指定記憶體要求對齊的,通過這裡可以知道,初始化pucAlignedHeap時並不是一定等於&ucHeap[0]的,而是會根據位元組對齊的要求,在&ucHeap[0]和&ucHeap[portBYTE_ALIGNMENT]之間,這就會導致ucHeap的前幾個位元組可能會被浪費到,這也是為什麼會有一個configADJUSTED_HEAP_SIZE


(4)xNextFreeByte   記錄已經使用的記憶體數量

舉個例子:
假設記憶體堆ucHeap[256]被系統分配為:0x20000603 ~0x20000702  大小為0x100個位元組  
因為記憶體管理要求位元組對齊(8),所以pucAlignedHeap 為0x20000608作為後面記憶體操作的起始地址。   configADJUSTED_HEAP_SIZE=256(0x100)-8 =248個表示可以操作的記憶體的安全範圍。
則結束地址為0x20000700   則記憶體堆實際的可活動範圍為 0x20000608~0x20000700

獲取記憶體:加入申請記憶體 30個位元組  則為其分配32個位元組,滿足其位元組對齊要求
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
xNextFreeByte +=xWantedSize //記錄使用記憶體數量

**************************************/
//pvPortMalloc()實現起來非常簡單,需要注意的是分配記憶體之後不允許再釋放
#include <stdlib.h>
#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE
#include "FreeRTOS.h"
#include "task.h"
#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE

#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )  //必須是預設支援動態分配的,否則編譯會報錯的
	#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif

	
//由於可能出現的記憶體對齊的問題,所以直接捨棄一個對齊單位.
//這樣導致實際的陣列大小可能小於申請的大小	
/* A few bytes might be lost to byte aligning the heap start address. */
#define configADJUSTED_HEAP_SIZE	( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )  



/* Allocate the memory for the heap. */
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )   // 1 使能自定義堆疊 0編譯器決定
	/* The application writer has already defined the array used for the RTOS
	heap - probably so it can be placed in a special segment or address. */
	extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
	static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */


		
/* Index into the ucHeap array. */
static size_t xNextFreeByte = ( size_t ) 0;     //表示已經使用過的記憶體數量

//  static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ] 由系統分配陣列
//	configADJUSTED_HEAP_SIZE 為可用的實際大小  xNextFreeByte記錄了使用的記憶體數量
/*---------申請記憶體實際上是從ucHeap 中獲取一塊記憶體,然後返回---------*/

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
static uint8_t *pucAlignedHeap = NULL;
  
	/* Ensure that blocks are always aligned to the required number of bytes. */
	#if( portBYTE_ALIGNMENT != 1 )
	{
		if( xWantedSize & portBYTE_ALIGNMENT_MASK )
		{
			/* Byte alignment required. */
			xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
		} //如果對齊的巨集定義了,則要求申請的數量是  portBYTE_ALIGNMENT 的整數倍。 
	}
	#endif

	vTaskSuspendAll();//掛起排程器任務
	{
		if( pucAlignedHeap == NULL )
		{
			/* Ensure the heap starts on a correctly aligned boundary. */
			pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] )\
			& ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
			//思考一下:為什麼取的 ucHeap[ portBYTE_ALIGNMENT ]的地址而不是 ucHeap[0]的地址。
			//首地址需要對齊: 1-7 均要被捨棄 ,取ucHeap[ portBYTE_ALIGNMENT ]可以保證捨棄後,滿足8位對齊且不越界
			
		}
     
		//判斷 (已使用的 + 新申請的 < 能提供的 )且(已使用的 + 新申請的 >  已使用的)  
		/* Check there is enough room left for the allocation. */
		if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&
			( ( xNextFreeByte + xWantedSize ) > xNextFreeByte )	)/* Check for overflow. */
		{
			/* Return the next free byte then increment the index past this
			block. */
			pvReturn = pucAlignedHeap + xNextFreeByte;
			xNextFreeByte += xWantedSize;
		}

		traceMALLOC( pvReturn, xWantedSize );
	}
	( void ) xTaskResumeAll();

	//如果定義了鉤子函式,那麼申請失敗時就呼叫鉤子函式
	#if( configUSE_MALLOC_FAILED_HOOK == 1 )   //申請失敗的函式鉤子
	{
		if( pvReturn == NULL )
		{
			extern void vApplicationMallocFailedHook( void );
			vApplicationMallocFailedHook();
		}
	}
	#endif

	return pvReturn;
}
/*-----------------------------------------------------------*/
/*
vPortFree 什麼也不做,xNextFreeByte只會增加,不會減小,因此陣列中的記憶體一旦使用,就再也不會被回收
*/

void vPortFree( void *pv )
{
	/* Memory cannot be freed using this scheme.  See heap_2.c, heap_3.c and
	heap_4.c for alternative implementations, and the memory management pages of
	http://www.FreeRTOS.org for more information. */
	( void ) pv;

	/* Force an assert as it is invalid to call this function. */
	configASSERT( pv == NULL );
}




/*-----------------------------------------------------------*/
/* 初始化,由於所有的記憶體都是靜態的,因此只要對xNextFreeByte進行賦值即可 */

void vPortInitialiseBlocks( void )
{
	/* Only required when static memory is not cleared. */
	xNextFreeByte = ( size_t ) 0;
}




/*-----------------------------------------------------------*/
/*獲取當前可用記憶體 */
size_t xPortGetFreeHeapSize( void )
{
	return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}