FreeRTOS---記憶體管理筆記
FreeRTOS分為5種記憶體管理方式,在這裡記錄對於記憶體管理方式的理解。
一、heap_1
1、heap_1,只允許管理一個靜態的陣列ucHeap,記憶體從靜態Ram中由系統分配,不能指定管理外部SRAM,或者管理堆中的記憶體
2、直接按照申請的記憶體的大小,從空閒記憶體中劃分記憶體
3、分配的記憶體無法回收
這種記憶體分配方式最簡單直接,速度快程式簡單。適用於分配完記憶體後不需要回收的場合。
#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 //heap_1.h的記憶體管理方式,只允許管理一個靜態的陣列ucHeap,記憶體從靜態Ram中由系統分配 #if( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 #endif //首地址按portBYTE_ALIGNMENT對齊後記憶體容量的大小 #define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) #if( configAPPLICATION_ALLOCATED_HEAP == 1 ) extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; #else //heap_1記憶體分配,直接由Ram中劃分一個數組,進行記憶體管理。 static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; #endif //當前佔用記憶體數 static size_t xNextFreeByte = ( size_t ) 0; /*-----------------------------------------------------------*/ void *pvPortMalloc( size_t xWantedSize ) { //此指標指向分配的地址 void *pvReturn = NULL; //按portBYTE_ALIGNMENT對齊後記憶體首地址 static uint8_t *pucAlignedHeap = NULL; //對齊方式不為1的,申請記憶體數需要按照portBYTE_ALIGNMENT對齊 #if( portBYTE_ALIGNMENT != 1 ) { //相當於xWantedSize%portBYTE_ALIGNMENT if( xWantedSize & portBYTE_ALIGNMENT_MASK ) { //將xWantedSize按照portBYTE_ALIGNMENT強制對齊 xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } } #endif //掛起任務防止重入 vTaskSuspendAll(); { //pucAlignedHeap儲存按portBYTE_ALIGNMENT對齊後的有效記憶體首地址 if( pucAlignedHeap == NULL ) { pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); } //邊界檢查,檢查當前剩餘記憶體容量是否足夠分配,且檢查xWantedSize是否為正數 if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) ) { //將剩餘記憶體的首地址返回給使用者 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; } /*-----------------------------------------------------------*/ void vPortFree( void *pv ) { //什麼都不做,因為heap_1分配的記憶體不需要回收 ( void ) pv; configASSERT( pv == NULL ); } /*-----------------------------------------------------------*/ void vPortInitialiseBlocks( void ) { //將表示已佔用記憶體數的區域性變數清零 //如果清零後再次呼叫pvPortMalloc();將會出錯 //可能導致兩個已分配指標使用同一塊記憶體 xNextFreeByte = ( size_t ) 0; } /*-----------------------------------------------------------*/ size_t xPortGetFreeHeapSize( void ) { //用於獲取當前剩餘可用記憶體大小 return ( configADJUSTED_HEAP_SIZE - xNextFreeByte ); }
二、heap_2
1、使用連結串列管理,連結串列資料域為:有效記憶體數量 + 結點佔用空間
2、連結串列的頭結點、尾結點不存於管理的記憶體中,其他結點存於管理的記憶體中。頭結點指向記憶體中首個結點,首結點
指向尾結點。尾結點指標域為Null。首結點資料域為地址對齊後所有有效記憶體數。
3、記憶體的申請:
(1) 從連結串列中找到一個節點,它的資料域大於需要申請的記憶體數 + 結點佔用記憶體數(不能為尾結點)
(2) 將這個節點從連結串列中刪除,代表它已經分配給使用者
(3) 這個節點首地址減去結點本身佔用記憶體之後的地址,將會返回給使用者使用
(4) 判斷剩餘的記憶體,是否足夠再組成一個新的結點
(5) 如果能組成一個結點,則將新結點按照從小到大排列順序插入到連結串列,不能則返回
4、記憶體的釋放:
(1) 將使用者指標 - 結點佔用記憶體的大小,得到的就是管理此塊記憶體的結點的首地址。
(2) 將此結點插入到連結串列中
這種分配方式通過連結串列進行管理,支援記憶體的釋放,支援管理外部SRAM。如果申請跟釋放記憶體的大小是固定的,如32byte。頻繁的申請與釋放並不會造成碎片,且申請與釋放速度快。但如果申請記憶體不固定,頻繁申請與釋放將會造成記憶體碎片。
#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 #if( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 #endif //首地址按portBYTE_ALIGNMENT對齊後記憶體容量大小 #define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) static void prvHeapInit( void ); //heap_2允許管理指定地址的記憶體 #if( configAPPLICATION_ALLOCATED_HEAP == 1 ) extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; #else static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; #endif //連結串列結構體定義 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; //將連結串列結構體的大小按照portBYTE_ALIGNMENT進行對齊 static const uint16_t heapSTRUCT_SIZE = ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); //一個連結串列結點,佔用記憶體最小為2倍的結點大小 #define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( heapSTRUCT_SIZE * 2 ) ) //建立連結串列表頭跟表尾 static BlockLink_t xStart, xEnd; //此變數記錄空閒記憶體容量 static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE; //此巨集定義,將一個節點插入連結串列 //順序為記憶體從小到大排列,防止在遍歷結點時,使用者申請小容量記憶體卻佔用大容量記憶體的結點 #define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ { \ BlockLink_t *pxIterator; \ size_t xBlockSize; \ \ xBlockSize = pxBlockToInsert->xBlockSize; \ \ \ //遍歷連結串列,找到一個節點pxIterator,它指向的結點的xBlockSize大於或等於需要插入的結點 for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock ) \ { \ /* There is nothing to do here - just iterate to the correct position. */ \ } \ \ //將需要插入的結點pxBlockToInsert,插入到pxIterator的後面 pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock; \ pxIterator->pxNextFreeBlock = pxBlockToInsert; \ } /*-----------------------------------------------------------*/ void *pvPortMalloc( size_t xWantedSize ) { //定義三個結點,pxBlock為xBlockSize大於等於申請記憶體數的結點 //pxPreviousBlock為pxBlock上一結點,pxNewBlockLink為從pxBlock分割的新建結點 BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink; //定義一個靜態變數xHeapHasBeenInitialised,初始值為0,用作初始化heap的標誌位 static BaseType_t xHeapHasBeenInitialised = pdFALSE; void *pvReturn = NULL; vTaskSuspendAll(); { //判斷是否需要初始化heap if( xHeapHasBeenInitialised == pdFALSE ) { prvHeapInit(); xHeapHasBeenInitialised = pdTRUE; } if( xWantedSize > 0 ) { //申請的記憶體,加上結點結構體佔用的記憶體,即為所需的總記憶體大小(xWantedSize) xWantedSize += heapSTRUCT_SIZE; //申請記憶體數,按照portBYTE_ALIGNMENT_MASK對齊 if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0 ) { xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); } } if( ( xWantedSize > 0 ) && ( xWantedSize < configADJUSTED_HEAP_SIZE ) ) { //頭結點開始遍歷連結串列,找到記憶體大於或等於xWantedSize的結點pxBlock //pxPreviousBlock為pxBlock前結點 pxPreviousBlock = &xStart; pxBlock = xStart.pxNextFreeBlock; while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) { pxPreviousBlock = pxBlock; pxBlock = pxBlock->pxNextFreeBlock; } //pxBlock不能為尾結點 if( pxBlock != &xEnd ) { //pxBlock首地址 + 結點結構體大小,即為有效記憶體首地址 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + heapSTRUCT_SIZE ); //pxPreviousBlock的指標域指向pxBlock的下一結點 //相當於將pxBlock結點從連結串列中刪除,因為結點pxBlock已經分配給使用者了 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; //判斷結點剩餘記憶體是否大於heapMINIMUM_BLOCK_SIZE,大於則新建結點 if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE ) { //新建結點,首地址為pxBlock剩餘記憶體的首地址 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); //在剩餘記憶體首地址建立一個節點,BlockSize為剩餘記憶體 pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; //pxBlock的xBlockSize大小改為xWantedSize pxBlock->xBlockSize = xWantedSize; //新的結點插入到連結串列中 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 ) { //獲取此記憶體塊的結點的首地址 puc -= heapSTRUCT_SIZE; //將結點首地址賦值給pxLink pxLink = ( void * ) puc; vTaskSuspendAll(); { //將此結點插入到空閒連結串列中 prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) ); //更新剩餘記憶體數 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. */ } /*-----------------------------------------------------------*/ //heap初始化函式,初始化頭結點與尾結點 //且在有效記憶體首地址建立一個節點,頭結點指向它,它指向尾結點,xBlockSize為對齊後有效記憶體總數 static void prvHeapInit( void ) { BlockLink_t *pxFirstFreeBlock; uint8_t *pucAlignedHeap; //pucAlignedHeap為記憶體按portBYTE_ALIGNMENT對齊後的有效記憶體首地址 pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); //連結串列頭結點指向有效記憶體的首地址 xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; xStart.xBlockSize = ( size_t ) 0; //連結串列尾結點指標域為空,資料域為有效記憶體數 xEnd.xBlockSize = configADJUSTED_HEAP_SIZE; xEnd.pxNextFreeBlock = NULL; //在對齊地址後的有效記憶體首地址,建立一個結點 //結點指標域指向尾結點,資料域為對齊地址後有效記憶體容量 pxFirstFreeBlock = ( void * ) pucAlignedHeap; pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE; pxFirstFreeBlock->pxNextFreeBlock = &xEnd; } /*-----------------------------------------------------------*/
三、heap_3
1、此管理方式封裝標準庫中的記憶體管理函式,管理的是啟動檔案中設定的heap。
封裝過程中新增的是掛起、恢復排程器的函式,用來保護執行緒。
此外,它並不能顯示我們剩餘的空閒記憶體的大小
四、heap_4
1、跟heap_2一樣採用連結串列進行管理
2、增加了記憶體塊的合併演算法,滿足條件下先合併前結點再合併後結點,消除記憶體碎片
3、結點資料域最高位作為標誌位,用於檢查記憶體塊的狀態,為1代表被佔用,0則空閒
4、連結串列中結點按地址從小到大排序,與heap_2的按記憶體從小到大排列不同
5、因為遍歷結點的條件是地址大小。不同於heap_2按照記憶體大小,所以尾結點放置於記憶體中,用於結束遍歷
#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
#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
#if( configAPPLICATION_ALLOCATED_HEAP == 1 )
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif
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;
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
static void prvHeapInit( void );
/*-----------------------------------------------------------*/
//結點大小,按portBYTE_ALIGNMENT對齊
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
//頭結點位於Ram中,尾結點位於管理的記憶體中
static BlockLink_t xStart, *pxEnd = NULL;
//記錄可用記憶體數
static size_t xFreeBytesRemaining = 0U;
//記錄剩餘記憶體歷史最小值
static size_t xMinimumEverFreeBytesRemaining = 0U;
//用於判斷以及改變結點資料域最高位
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/
void *pvPortMalloc( size_t xWantedSize )
{
//pxBlock為分配記憶體給使用者的結點,pxPreviousBlock為pxBlock的前結點,pxNewBlockLink為新建結點
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
//此指標返回分配的記憶體給使用者
void *pvReturn = NULL;
vTaskSuspendAll();
{
//初始化heap
if( pxEnd == NULL )
{
prvHeapInit();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//申請記憶體數不能影響到標誌位
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{
if( xWantedSize > 0 )
{
xWantedSize += xHeapStructSize;
//申請記憶體數按portBYTE_ALIGNMENT對齊
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
//找到剩餘記憶體大於申請記憶體的結點pxBlock
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != pxEnd )
{
//返回pxBlock中記憶體的首地址
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
//pxBlock的前結點改為指向pxBlock的後結點,pxBlock從連結串列中刪除
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
//如果pxBlock剩餘記憶體足夠建立一個新的節點
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
//新節點首地址為使用者申請的記憶體後的首地址
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
//一個巨集定義,如果新建的結點首地址不按照portBYTE_ALIGNMENT對齊,進入死迴圈
//#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
//計算新建結點擁有的記憶體
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//更新pxBlock的記憶體塊大小
pxBlock->xBlockSize = xWantedSize;
//將新建的結點按照地址從小到大插入到連結串列
prvInsertBlockIntoFreeList( pxNewBlockLink );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//更新剩餘記憶體大小
xFreeBytesRemaining -= pxBlock->xBlockSize;
//更新最小剩餘記憶體的記錄
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//pxBlock的資料域最高位置1,代表此記憶體塊被佔用
pxBlock->xBlockSize |= xBlockAllocatedBit;
pxBlock->pxNextFreeBlock = NULL;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
return pvReturn;
}
/*-----------------------------------------------------------*/
void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
//指標地址減去連結串列結構體的大小,為指標所在結點的首地址
puc -= xHeapStructSize;
//pxLink為結點首地址
pxLink = ( void * ) puc;
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL );
//如果結點資料域標誌位為1,代表此結點記憶體被佔用,進行釋放操作
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
{
//檢查是否為可釋放的結點
if( pxLink->pxNextFreeBlock == NULL )
{
//將資料域最高位置0,表示此結點為空閒態
pxLink->xBlockSize &= ~xBlockAllocatedBit;
vTaskSuspendAll();
{
//剩餘記憶體數更新
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
//將此結點插入到連結串列
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
}
( void ) xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
size_t xPortGetMinimumEverFreeHeapSize( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
void vPortInitialiseBlocks( void )
{
/* This just exists to keep the linker quiet. */
}
/*-----------------------------------------------------------*/
static void prvHeapInit( void )
{
BlockLink_t *pxFirstFreeBlock;
uint8_t *pucAlignedHeap;
size_t uxAddress;
size_t xTotalHeapSize = configTOTAL_HEAP_SIZE;
uxAddress = ( size_t ) ucHeap;
//首地址按照portBYTE_ALIGNMENT對齊
if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
{
uxAddress += ( portBYTE_ALIGNMENT - 1 );
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
//總記憶體大小減去調整後首地址捨去的記憶體,尾部不捨去,與前幾種對齊方式不同
xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
}
pucAlignedHeap = ( uint8_t * ) uxAddress;
//頭結點指向記憶體起始地址
xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
//記憶體尾部預留建立一個節點的記憶體
//因為連結串列按照地址遍歷,所以尾結點放置於記憶體中
uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
uxAddress -= xHeapStructSize;
//地址上移,按照portBYTE_ALIGNMENT對齊
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
pxEnd = (void *) uxAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
//記憶體首地址建立結點,它的有效記憶體為記憶體起始地址到尾結點首地址
pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
//標誌位為size_t資料型別的最高位置1
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
//定義一個結點指標
BlockLink_t *pxIterator;
uint8_t *puc;
//遍歷連結串列,找到一個結點pxIterator,它指向的結點的地址大於pxBlockToInsert,稱為前結點
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
/* Nothing to do here, just iterate to the right position. */
}
/************************ 結點的合併 **************************/
//如果前結點,跟pxBlockToInsert連續,pxBlockToInsert將被前結點合併
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
//被前結點合併,前結點的指標域不需要更新
//前結點資料域增加
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
//pxBlockToInsert與前結點為同一結點
pxBlockToInsert = pxIterator;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//如果pxBlockToInsert跟找到的結點指向的下一結點(後結點)連續,則pxBlockToInsert合併後結點
puc = ( uint8_t * ) pxBlockToInsert;
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
//pxBlockToInsert資料域增加
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
//pxBlockToInsert合併後結點,被插入結點需要更新指標域
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
//如果跟尾結點連續,則指標域改成指向尾結點
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
/************************ 結點的插入 **************************/
else
{
//如果不連續,pxBlockToInsert指向後結點
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
//前結點跟pxBlockToInsert不同,代表前結點跟pxBlockToInsert不連續
if( pxIterator != pxBlockToInsert )
{
//前結點指向pxBlockToInsert
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
五、heap_5
1、記憶體的申請、釋放,結點的插入,與heap_4一致。
2、記憶體的初始化不同,heap_5可以初始化多個地址不連續的記憶體塊,一同管理。
前一記憶體塊尾結點指向後一記憶體塊頭結點,最後一個記憶體塊尾結點為Null
3、初始化之前,需要使用者自定義記憶體塊地址以及大小
4、初始化函式不能重複執行,以及未初始化之前不能申請記憶體,否則都會進入死迴圈
#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
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
///* Used by heap_5.c. */
//typedef struct HeapRegion
//{
// uint8_t *pucStartAddress; //指向一個地址
// size_t xSizeInBytes; //此記憶體塊的大小
//} HeapRegion_t;
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;
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
/*-----------------------------------------------------------*/
//連結串列結構體大小按照portBYTE_ALIGNMENT對齊
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
//定義頭結點、尾結點指標,尾結點位於記憶體中
static BlockLink_t xStart, *pxEnd = NULL;
//記錄剩餘記憶體
static size_t xFreeBytesRemaining = 0U;
//記錄歷史最小剩餘記憶體
static size_t xMinimumEverFreeBytesRemaining = 0U;
//結點記憶體狀態位標誌
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/
void *pvPortMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
//pxEnd為空證明未執行初始化,進入死迴圈
configASSERT( pxEnd );
vTaskSuspendAll();
{
//申請記憶體數不能大於2GB,影響記憶體狀態標誌位
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{
//申請記憶體數加上結點佔用記憶體後,按照portBYTE_ALIGNMENT對齊
if( xWantedSize > 0 )
{
xWantedSize += xHeapStructSize;
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
//遍歷結點,找到記憶體數大於等於申請記憶體數的結點pxBlock,將其分配給使用者
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != pxEnd )
{
//pxBlock中的記憶體首地址返回給使用者
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
//pxBlock的前結點改為指向pxBlock的後結點,pxBlock從連結串列中刪除
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
//如果pxBlock中剩餘記憶體大於新建一個節點所需記憶體
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
//新建一個節點,位於pxBlock剩餘記憶體的首地址
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
//計算新建結點的記憶體數
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
//更新pxBlock的記憶體數
pxBlock->xBlockSize = xWantedSize;
//將新建的結點插入到連結串列,順序為按照地址從小到大
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//更新剩餘記憶體數
xFreeBytesRemaining -= pxBlock->xBlockSize;
//更新最小剩餘記憶體歷史
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//pxBlock資料域最高位置1,標誌此結點記憶體被佔用
pxBlock->xBlockSize |= xBlockAllocatedBit;
//被分配的結點,指標域設為Null
pxBlock->pxNextFreeBlock = NULL;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceMALLOC( pvReturn, xWantedSize );
}
( void ) xTaskResumeAll();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
void vPortFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
//計算指標所處結點的首地址
puc -= xHeapStructSize;
//pxLink存放指標所處結點首地址
pxLink = ( void * ) puc;
//丟擲異常
configASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
configASSERT( pxLink->pxNextFreeBlock == NULL );
//如果結點的資料域標誌位為1,進行釋放操作
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
{
//如果結點的指標域為空
if( pxLink->pxNextFreeBlock == NULL )
{
//資料域標誌位更新為0
pxLink->xBlockSize &= ~xBlockAllocatedBit;
vTaskSuspendAll();
{
//更新空閒記憶體數
xFreeBytesRemaining += pxLink->xBlockSize;
traceFREE( pv, pxLink->xBlockSize );
//將結點插入到連結串列,順序為按地址從小到大
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
}
( void ) xTaskResumeAll();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/*-----------------------------------------------------------*/
size_t xPortGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
size_t xPortGetMinimumEverFreeHeapSize( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;
//遍歷連結串列,找到一個節點pxIterator,它指向的結點的地址大於pxBlockToInsert,稱為前結點
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
/* Nothing to do here, just iterate to the right position. */
}
//如果pxBlockToInsert,與前結點連續,則合併兩個結點,合併後結點首地址為前結點首地址
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
//更新合併後的結點的資料域
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
//pxBlockToInsert的指標域,指向合併後結點的首地址
pxBlockToInsert = pxIterator;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
//如果pxBlockToInsert,與前結點指向的結點(後結點)連續,則合併兩個結點,合併後結點首地址為pxBlockToInsert的首地址
puc = ( uint8_t * ) pxBlockToInsert;
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
//更新pxBlockToInsert的資料域
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
//更新pxBlockToInsert的指標域
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
//後結點為尾結點,pxBlockToInsert指向尾結點
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
else
{
//如果pxBlockToInsert與後結點不連續,pxBlockToInsert指向後結點
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
//如果前結點與pxBlockToInsert沒有合併
if( pxIterator != pxBlockToInsert )
{
//前結點指向pxBlockToInsert,pxBlockToInsert成功插入到連結串列中
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/*-----------------------------------------------------------*/
//傳遞的結構體指標不可修改,且指標指向的記憶體也不可修改
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
{
//pxFirstFreeBlockInRegion快取當前記憶體塊的頭結點地址,pxPreviousFreeBlock快取上一個記憶體塊尾結點地址
BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock;
//記錄對齊地址後記憶體塊首地址
size_t xAlignedHeap;
//xTotalRegionSize代表的是本記憶體塊總的記憶體數,xTotalHeapSize代表所有記憶體塊記憶體數
size_t xTotalRegionSize, xTotalHeapSize = 0;
//記錄結構體陣列中的元素,從元素0到元素n,逐個初始化
BaseType_t xDefinedRegions = 0;
size_t xAddress;
//記錄結構體陣列每個元素的首地址
const HeapRegion_t *pxHeapRegion;
//尾結點不為Null進入死迴圈
configASSERT( pxEnd == NULL );
//取結構體陣列第一個元素首地址
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
//初始化結構體陣列,當陣列最後一個元素的xSizeInBytes為0,代表所有記憶體塊初始化完畢
//xSizeInBytes不為0則持續初始化記憶體塊
while( pxHeapRegion->xSizeInBytes > 0 )
{
//記錄單個記憶體塊的記憶體大小
xTotalRegionSize = pxHeapRegion->xSizeInBytes;
//記錄當前記憶體塊的首地址
xAddress = ( size_t ) pxHeapRegion->pucStartAddress;
//地址按照portBYTE_ALIGNMENT對齊
if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xAddress += ( portBYTE_ALIGNMENT - 1 );
xAddress &= ~portBYTE_ALIGNMENT_MASK;
//更新記憶體大小
xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;
}
//記錄經過對齊後當前記憶體塊的首地址
xAlignedHeap = xAddress;
//如果當前為結構體陣列第0元素
if( xDefinedRegions == 0 )
{
//頭結點初始化,指向此記憶體塊首地址
xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
}
else //否則檢查尾結點與結構體陣列元素的地址是否異常
{
configASSERT( pxEnd != NULL );
configASSERT( xAddress > ( size_t ) pxEnd );
}
//指向尾結點,當初始化第二塊記憶體塊
//此指標指向的是上一記憶體塊尾結點
pxPreviousFreeBlock = pxEnd;
//在此記憶體塊尾部建立一個結點
xAddress = xAlignedHeap + xTotalRegionSize;
xAddress -= xHeapStructSize;
xAddress &= ~portBYTE_ALIGNMENT_MASK;
//尾結點指標指向當前記憶體塊尾部
pxEnd = ( BlockLink_t * ) xAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
//指向當前記憶體塊首地址建立一個結點,此結點稱為當前記憶體塊首結點
pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
//將當前記憶體塊的記憶體數裝載進首結點資料域
pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;
//首結點指向當前記憶體塊尾結點
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
//當初始化第一個記憶體塊,下面語句不被執行
if( pxPreviousFreeBlock != NULL )
{
//讓前一記憶體塊的尾結點指向後一記憶體塊的首結點
pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
}
//初始化完畢一個記憶體塊,更新總記憶體塊的記憶體大小
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
xDefinedRegions++;
//指向結構體陣列中下一個元素,對下一記憶體塊進行初始化
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
}
//更新剩餘記憶體數,以及歷史最小剩餘記憶體數
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
xFreeBytesRemaining = xTotalHeapSize;
configASSERT( xTotalHeapSize );
//根據size_t資料型別佔用位元組數更新結點記憶體狀態位標誌
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}
六、myMalloc
將heap_5修改了一下,可脫離FreeRTOS使用。方便進行記憶體管理
標頭檔案
#ifndef __bsp_Malloc_H
#define __bsp_Malloc_H
#include <stdlib.h>
#include "stm32f1xx_hal.h"
//定義管理的記憶體塊的起始地址與大小
#define Ram1Addr (0x68000000UL)
#define Ram1Size (960*1024)
//定義中斷遮蔽優先順序
#define configMAX_INTERRUPT_PRIORITY 2
#ifndef TEST_MARKER
#define TEST_MARKER()
#endif
#ifndef myFORCE_INLINE
#define myFORCE_INLINE __forceinline
#endif
typedef struct myHeapRegion
{
uint8_t *pucStartAddress; //指向一個地址
size_t xSizeInBytes; //此記憶體塊的大小
} myHeapRegion_t;
extern myHeapRegion_t xHeapRegions[];
void *myMalloc( size_t xWantedSize );
void myFree( void *pv );
size_t myGetFreeHeapSize( void );
size_t myGetMinimumEverFreeHeapSize( void );
void myDefineHeapRegions( const myHeapRegion_t * const pxHeapRegions );
#endif
原始檔
#include "bsp_Malloc.h"
typedef long BaseType_t;
#define configUSE_MALLOC_FAILED_HOOK 0
#define heapMINIMUM_BLOCK_SIZE ( ( size_t ) ( xHeapStructSize << 1 ) )
#define heapBITS_PER_BYTE ( ( size_t ) 8 )
#define portBYTE_ALIGNMENT 8
#define portBYTE_ALIGNMENT_MASK (portBYTE_ALIGNMENT - 1)
#define MallocInterruptMask( ) vPortSetBASEPRI( )
#define MallocInterruptRelease( ) vPortRaiseBASEPRI( )
#define mallocASSERT( x ) if ((x) == 0) { for( ;; );}
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;
myHeapRegion_t xHeapRegions[] =
{
{ ( uint8_t * ) Ram1Addr, Ram1Size },
{ NULL, 0 }
};
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert );
/*-----------------------------------------------------------*/
static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( portBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
static BlockLink_t xStart, *pxEnd = NULL;
static size_t xFreeBytesRemaining = 0U;
static size_t xMinimumEverFreeBytesRemaining = 0U;
static size_t xBlockAllocatedBit = 0;
/*-----------------------------------------------------------*/
// 開中斷
// 向basepri中寫入0就表示開中斷
static myFORCE_INLINE void vPortSetBASEPRI( void )
{
__asm
{
msr basepri, 0
}
}
// 關中斷
// 向basepri中寫入configMAX_INTERRUPT_PRIORITY,
// 表明優先順序低於configMAX_INTERRUPT_PRIORITY的中斷都會被遮蔽
static myFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_INTERRUPT_PRIORITY;
__asm
{
msr basepri, ulNewBASEPRI
dsb
isb
}
}
void *myMalloc( size_t xWantedSize )
{
BlockLink_t *pxBlock, *pxPreviousBlock, *pxNewBlockLink;
void *pvReturn = NULL;
mallocASSERT( pxEnd );
MallocInterruptMask();
{
if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
{
if( xWantedSize > 0 )
{
xWantedSize += xHeapStructSize;
if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
{
xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
{
pxPreviousBlock = &xStart;
pxBlock = xStart.pxNextFreeBlock;
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
{
pxPreviousBlock = pxBlock;
pxBlock = pxBlock->pxNextFreeBlock;
}
if( pxBlock != pxEnd )
{
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
{
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
pxBlock->xBlockSize = xWantedSize;
prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
}
else
{
TEST_MARKER();
}
xFreeBytesRemaining -= pxBlock->xBlockSize;
if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
{
xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
}
else
{
TEST_MARKER();
}
pxBlock->xBlockSize |= xBlockAllocatedBit;
pxBlock->pxNextFreeBlock = NULL;
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
}
MallocInterruptRelease();
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
if( pvReturn == NULL )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
else
{
TEST_MARKER();
}
}
#endif
return pvReturn;
}
/*-----------------------------------------------------------*/
void myFree( void *pv )
{
uint8_t *puc = ( uint8_t * ) pv;
BlockLink_t *pxLink;
if( pv != NULL )
{
puc -= xHeapStructSize;
pxLink = ( void * ) puc;
mallocASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
mallocASSERT( pxLink->pxNextFreeBlock == NULL );
if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
{
if( pxLink->pxNextFreeBlock == NULL )
{
pxLink->xBlockSize &= ~xBlockAllocatedBit;
MallocInterruptMask();
{
xFreeBytesRemaining += pxLink->xBlockSize;
prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
}
MallocInterruptRelease();
}
else
{
TEST_MARKER();
}
}
else
{
TEST_MARKER();
}
}
}
/*-----------------------------------------------------------*/
size_t myGetFreeHeapSize( void )
{
return xFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
size_t myGetMinimumEverFreeHeapSize( void )
{
return xMinimumEverFreeBytesRemaining;
}
/*-----------------------------------------------------------*/
static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert )
{
BlockLink_t *pxIterator;
uint8_t *puc;
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
/* Nothing to do here, just iterate to the right position. */
}
puc = ( uint8_t * ) pxIterator;
if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
{
pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
pxBlockToInsert = pxIterator;
}
else
{
TEST_MARKER();
}
puc = ( uint8_t * ) pxBlockToInsert;
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
{
if( pxIterator->pxNextFreeBlock != pxEnd )
{
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
}
else
{
pxBlockToInsert->pxNextFreeBlock = pxEnd;
}
}
else
{
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
}
if( pxIterator != pxBlockToInsert )
{
pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
TEST_MARKER();
}
}
/*-----------------------------------------------------------*/
void myDefineHeapRegions( const myHeapRegion_t * const pxHeapRegions )
{
BlockLink_t *pxFirstFreeBlockInRegion = NULL, *pxPreviousFreeBlock;
size_t xAlignedHeap;
size_t xTotalRegionSize, xTotalHeapSize = 0;
BaseType_t xDefinedRegions = 0;
size_t xAddress;
const myHeapRegion_t *pxHeapRegion;
mallocASSERT( pxEnd == NULL );
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
while( pxHeapRegion->xSizeInBytes > 0 )
{
xTotalRegionSize = pxHeapRegion->xSizeInBytes;
xAddress = ( size_t ) pxHeapRegion->pucStartAddress;
if( ( xAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
{
xAddress += ( portBYTE_ALIGNMENT - 1 );
xAddress &= ~portBYTE_ALIGNMENT_MASK;
xTotalRegionSize -= xAddress - ( size_t ) pxHeapRegion->pucStartAddress;
}
xAlignedHeap = xAddress;
if( xDefinedRegions == 0 )
{
xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;
}
else
{
mallocASSERT( pxEnd != NULL );
mallocASSERT( xAddress > ( size_t ) pxEnd );
}
pxPreviousFreeBlock = pxEnd;
xAddress = xAlignedHeap + xTotalRegionSize;
xAddress -= xHeapStructSize;
xAddress &= ~portBYTE_ALIGNMENT_MASK;
pxEnd = ( BlockLink_t * ) xAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;
pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
pxFirstFreeBlockInRegion->xBlockSize = xAddress - ( size_t ) pxFirstFreeBlockInRegion;
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
if( pxPreviousFreeBlock != NULL )
{
pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
}
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
xDefinedRegions++;
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
}
xMinimumEverFreeBytesRemaining = xTotalHeapSize;
xFreeBytesRemaining = xTotalHeapSize;
mallocASSERT( xTotalHeapSize );
xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );
}
程式在原子戰艦V3通過測試:
觀察斷點,可知 j 比 k 大0x38,申請記憶體數為4、11、4,進行對齊後應該是8、16、8,加上3個結點佔用的空間:24
共佔用空間:56 = 0x38。 全部記憶體釋放後,容量為0xEFFF8,剛好為:0xF0000 - 0x08。丟失的8位元組被尾結點佔用。
相關推薦
FreeRTOS---記憶體管理筆記
FreeRTOS分為5種記憶體管理方式,在這裡記錄對於記憶體管理方式的理解。 一、heap_1 1、heap_1,只允許管理一個靜態的陣列ucHeap,記憶體從靜態Ram中由系統分配,不能指定管理外部SRAM,或者管理堆中的記憶體 2、直接按照申請的記憶體的大小
第四節:FreeRTOS 記憶體管理
目錄 記憶體管理的介紹 記憶體碎片 Heap_1-5記憶體分配的區別 Heap_1:適用於一旦建立好記憶體,就不刪除的任務。 (本質是分配的大陣列做記憶體堆.) Heap_2:適用於重複分配和刪除具有相同堆疊空間任務。(本質是分配的大
FreeRTOS(19)---FreeRTOS 記憶體管理分析
FreeRTOS 記憶體管理分析 FreeRTOS 記憶體管理分析 heap_1.c 記憶體申請:pvPortMalloc() 獲取當前未分配的記憶體堆大小:xPortGetFreeHeapSize() 其它函
FreeRTOS高階篇7---FreeRTOS記憶體管理分析
記憶體管理對應用程式和作業系統來說都非常重要。現在很多的程式漏洞和執行崩潰都和記憶體分配使用錯誤有關。 FreeRTOS作業系統將核心與記憶體管理分開實現,作業系統核心僅規定了必要的記憶體管理函式原型,而不關心這些記憶體管理函式是如何實現的。這樣做大有好處,可以
Objective-c的記憶體管理筆記
最近有在學objective-c,當然是為了學ios的程式開發,主要是iphone的,在看到記憶體管理這方面的知識時, 發現還是和C有點不一樣的,所以在看了一些資料後,整理一下筆記,以便於自己以後檢視。 此文涉及的記憶體管理是針對於繼承於NSObject的Class。 Ob
FreeRTOS記憶體管理
FreeRTOS提供了幾個記憶體堆管理方案,有複雜的也有簡單的。其中最簡單的管理策略也能滿足很多應用的要求,比如對安全要求高的應用,這些應用根本不允許動態記憶體分配的。 FreeRTOS也允許你自己實現記憶體堆管理,甚至允許你同時使用兩種記憶體堆管理方案。同
FreeRTOS筆記(十三)記憶體管理
文章目錄 01 - 記憶體管理 02 - 範例 03 - 總結 上一文連結:FreeRTOS筆記(十二)資源管理 01 - 記憶體管理 回顧一個最開初沒有提問的問題,我們建立任務的時候,會生成一個TCB任務控制塊,它需要在
[讀書筆記]iOS與OS X多執行緒和記憶體管理 [GCD部分]
3.2 GCD的API 蘋果對GCD的說明:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。 “Dispatch Queue”是執行處理的等待佇列。通過dispatch_async函式等API,在Block
記憶體管理函式(C++學習筆記 14)
一、C語言中的記憶體管理函式 記憶體分配函式主要有malloc( )和calloc( ),記憶體釋放函式是free( )。 這兩類函式的原型在stdlib.h中,要使用這些函式時,首先要用檔案包含: #include"stdlib.h" 或#include<stdli
【Java】「深入理解Java虛擬機器」學習筆記(2)-記憶體管理
一、執行時資料區 JVM在執行Java程式的時候,將其執行時資料區劃分為若干不同區域。它們的用途和建立及銷燬的時間不同。 1、程式計數器(Program Counter Register) 是一塊很小的記憶體空間。當執行緒執行的是Java方法,它記錄的是當前正在執行的
程式設計師面試寶典隨筆記(一)--記憶體管理詳解
記憶體管理是C++最令人切齒痛恨的問題,也是C++最有爭議的問題,C++高手從中獲得了更好的效能,更大的自由,C++菜鳥的收穫則是一遍一遍的檢查程式碼和對C++的痛恨,但記憶體管理在C++中無處不在,記憶體洩漏幾乎在每個C++程式中都會發生,因此要想成為C++高手,記
S3C2440的記憶體管理單元MMU學習筆記
1.MMU簡介 MMU(Memory Management Unit),記憶體管理單元,主要職責: 將虛擬地址對映為實體地址,提供硬體機制的記憶體訪問許可權檢查 。MMU使得每個使用者程序擁有自己獨立的地址空間,並通過記憶體訪問許可權的檢查保護每個程序所用的
易學筆記-系統分析師考試-第3章 作業系統基本原理/3.3 記憶體管理/3.3.4 虛擬儲存管理
虛擬儲存管理 背景:固定式、分頁式、分段式儲存一個共同的特點是要求的儲存空間必須足夠大裝載入作業的全部資訊,但由於作業在執行過程中,作業中所有的記憶體不是一次全部使用的,甚至有些記憶體塊根本就不是使用,這樣就造成了記憶體資源的極度浪費 虛擬儲存工作過程:當作業載入到記憶體時
易學筆記-系統分析師考試-第3章 作業系統基本原理/3.3 記憶體管理/3.3.3 段頁式管理
分頁式儲存管理 概念:為了避免分割槽式管理產生儲存碎片和管理複雜的問題,分頁式管理把作業的邏輯地址劃分成若干個相等的區域(稱為頁),記憶體空間也劃分成若干個與頁長度相等的區域(也稱為頁幀或塊),然後把頁裝載到頁幀中 特點 頁幀可以是連續的,也可以是不連續的
易學筆記-系統分析師考試-第3章 作業系統基本原理/3.3 記憶體管理/3.3.2 分割槽儲存管理
記憶體儲存管理方式分類 分割槽儲存管理方式 分頁式儲存管理方式 分段式儲存管理方式 虛擬儲存器 分割槽儲存管理方式 固定分割槽 分割槽方法:在裝入作業前,記憶體被操作管理員分為N個區,分割槽大小和分割槽數量不可以修改
易學筆記-系統分析師考試-第3章 作業系統基本原理/3.3 記憶體管理/3.3.1 地址變換
幾種程式 源程式:使用者用開發語言編寫的程式 編譯程式(彙編程式):專門編譯源程式的程式 目標程式:編譯後的程式 地址 邏輯地址 概念:指的是目標程式使用的地址,也稱為相對地址或者虛擬地址 格式:一般以0為基地址
學習筆記- GC與記憶體管理
深入GC與記憶體管理 託管堆中存放引用型別物件,因此GC的記憶體管理的目標主要都是引用型別物件,本文中涉及的物件如無明確說明都指的是引用型別物件。 物件建立及生命週期 一個物件的生命週期簡單概括就是:建立>使用>釋放,在.NET中一個物件的生命週期: n
《現代作業系統》閱讀筆記——記憶體管理
地址重定位 最開始的計算機沒有重定位,程式直接使用記憶體的實體地址 任然被微波爐,洗衣機等嵌入式裝置使用 缺點是一次只能執行一個程式,因為第二個程式地址起始位置會變動 靜態重定位
Java虛擬機器筆記-1(Java技術體系&自動記憶體管理機制&記憶體區域與記憶體溢位&垃圾收集器與記憶體分配策略)
世界上沒有完美的程式,但寫程式是不斷追求完美的過程。 Devices(裝置、裝置)、GlassFish(商業相容應用伺服器) 目錄 1. Java技術體系包括: Java技術體系的4個平臺 虛擬機器分類 HotSpot VM 模組化、混合程式設計 多核並行
五、python學習筆記:記憶體管理
變數和記憶體管理的 細節, 包括: 變數無須事先宣告 變數無須指定型別 程式設計師不用關心記憶體管理 變數名會被“回收” del 語句能夠直接釋放資源 1、變數定義 變數只有被建立和賦值後才能被使用 2、動態型別 還要注意一點,Python 中不但變數名無需事