国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

這篇具有很好參考價值的文章主要介紹了手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

FreeRTOS中一共有5種內(nèi)存分配的方法,分別在文件heap_1.c,heap_2.c,
heap_3.c,heap_4.c,heap_5.c種。
雖然標準C庫中的 malloc()和 free()也可以實現(xiàn)動態(tài)內(nèi)存管理,但是它有以下缺陷:
1、在小型嵌入式系統(tǒng)種效率不高。
2、線程不安全。
3、具有不確定性,每次執(zhí)行的時間不同。
4、會導致內(nèi)存碎片。

FreeRTOS源碼解析集合(全網(wǎng)最詳細)
手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理
手把手教你FreeRTOS源碼詳解(二)——任務管理
手把手教你FreeRTOS源碼詳解(三)——隊列
手把手教你FreeRTOS源碼詳解(四)——信號量、互斥量、遞歸互斥量

1、heap_1.c

在heap_1.c種只實現(xiàn)了pvPortMalloc,不允許內(nèi)存釋放,相當于“靜態(tài)內(nèi)存”,適用于一旦創(chuàng)建好任務就不會刪除的應用,不會導致內(nèi)存碎片。
動態(tài)內(nèi)存分配需要一個內(nèi)存堆,F(xiàn)reeRTOS中的內(nèi)存堆為ucHeap[],
大小為configTOTAL_HEAP_SIZE,比如在heap_1.c中有以下代碼:

/*分配內(nèi)存堆空間(本質(zhì)是一個大數(shù)組),任務所需要的堆空間將從該內(nèi)存堆劃分*/
	extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];//如果configAPPLICATION_ALLOCATED_HEAP為1,則需用戶自行定義內(nèi)存堆
#else
	static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */

static size_t xNextFreeByte = ( size_t ) 0;

在FreeRTOS中內(nèi)存堆的實質(zhì)就是一個大數(shù)組,所有任務需要的堆空間將從該內(nèi)存堆中劃分,如果configAPPLICATION_ALLOCATED_HEAP = 1,則需要用戶自行定義內(nèi)存堆,否則將由編譯器決定。

1.1 內(nèi)存申請函數(shù)pvPortMalloc詳解

heap_1 的內(nèi)存申請函數(shù)pvPortMalloc()源碼如下:

void *pvPortMalloc( size_t xWantedSize )
{
void *pvReturn = NULL;
static uint8_t *pucAlignedHeap = NULL;

	/* 確保塊與所需字節(jié)數(shù)對齊      
	portBYTE_ALIGNMENT=8--8字節(jié)對齊
	portBYTE_ALIGNMENT_MASK=0x0007*/
	#if( portBYTE_ALIGNMENT != 1 )
	{
		if( xWantedSize & portBYTE_ALIGNMENT_MASK )//判斷xWantedSize是否需要對齊
		{
			/* 加上對齊所需的字節(jié)數(shù)以后,總共需要的字節(jié)數(shù) */
			xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
		}
	}
	#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 ) ) );
		}

		/* 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;
}
/*-----------------------------------------------------------*/

大部分硬件訪問內(nèi)存對齊的數(shù)據(jù)速度會更快,因此RTOS中首先對內(nèi)存進行對齊判斷:

	#if( portBYTE_ALIGNMENT != 1 )
	{
		if( xWantedSize & portBYTE_ALIGNMENT_MASK )//判斷xWantedSize是否需要對齊
		{
			/* 加上對齊所需的字節(jié)數(shù)以后,總共需要的字節(jié)數(shù) */
			xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
		}
	}
	#endif

Cortex-M3架構采用的8字節(jié)對齊,portBYTE_ALIGNMENT=8,對應的對齊掩碼portBYTE_ALIGNMENT_MASK=0x0007,首先對xWantedSize進行判斷是否需要對齊操作,如果需要對齊則補上對齊所需要的字節(jié)數(shù),比如xWantedSize為13,則實際分配時候劃分的空間大小應該為16,代碼流程如下:
13:0000 0000 0000 1101
0x0007:0000 0000 0000 0111
xWantedSize&portBYTE_ALIGNMENT_MASK=101&0111=0101=5
實際的xWantedSize=xWantedSize+(portBYTE_ALIGNMENT -5)=16

關閉任務調(diào)度器,防止分配內(nèi)存空間的過程被打亂。

vTaskSuspendAll();

第一次分配空間的時候,確保實際開始分配內(nèi)存的地址是對齊的。

if( pucAlignedHeap == NULL )
	{
		/* 確保實際開始分配內(nèi)存的地址是對齊的 */
		pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
	}

手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

如上圖,假如內(nèi)存堆的起始地址ucHeap為0x200006c4,起始地址不是對齊的,&ucHeap[ portBYTE_ALIGNMENT ]地址為0x200006c12,該地址
&(~portBYTE_ALIGNMENT_MASK)后,即將低3位置0后地址為0x200006c8,這個地址為實際開始分配內(nèi)存的地址,前4個字節(jié)的空間用于對齊棄掉了。
configADJUSTED_HEAP_SIZE為減去對齊棄去字節(jié)數(shù)后,大約的實際堆空間,由上述分析可知,實際的堆空間大于等于configADJUSTED_HEAP_SIZE。

#define configADJUSTED_HEAP_SIZE	( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT )

檢查當前空閑堆起始地址加上需要的空間是否小于總的內(nèi)存堆空間–溢出檢查

if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) &&( ( xNextFreeByte + xWantedSize ) > xNextFreeByte )	)

返回所申請內(nèi)存堆的起始地址,索引xNextFreeByte指向新的空閑堆的起始地址

		pvReturn = pucAlignedHeap + xNextFreeByte;
		xNextFreeByte += xWantedSize;

重新開啟任務調(diào)度器

		( void ) xTaskResumeAll();

內(nèi)存堆申請失敗的鉤子函數(shù),可由用戶自己來實現(xiàn)

#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
	if( pvReturn == NULL )
	{
		extern void vApplicationMallocFailedHook( void );
		vApplicationMallocFailedHook();
	}
}
#endif

1.2 vPortFree、vPortInitialiseBlocks、xPortGetFreeHeapSize函數(shù)

由于heap_1.c為靜態(tài)分配函數(shù),無法釋放內(nèi)存空間,vPortFree函數(shù)中確實也無具體操作

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 );
}

vPortInitialiseBlocks函數(shù)一般不需用調(diào)用,xNextFreeByte初始值已經(jīng)位0;

xPortGetFreeHeapSize函數(shù)為獲取空閑的堆空間大小

size_t xPortGetFreeHeapSize( void )
{
	return ( configADJUSTED_HEAP_SIZE - xNextFreeByte );
}

函數(shù)返回值為:總的堆空間大小減去已經(jīng)使用了的堆空間大小,即剩余的堆空間

2、heap_2.c

heap_2.c允許內(nèi)存的釋放,申請內(nèi)存時用了最佳匹配算法,但是其釋放內(nèi)存以后不會把相鄰的空閑塊合成一個大的塊,當系統(tǒng)不斷地申請和釋放大小不同的內(nèi)存塊時,會造成內(nèi)存碎片化,但是其效率也遠高于malloc()和 free()。
heap_2.c的內(nèi)存堆與heap_1.c相同,均是一個大數(shù)組!
heap_2.c為了能釋放內(nèi)存,引入了塊的概念,申請的每一個內(nèi)存空間就是一個內(nèi)存塊,內(nèi)存塊之間由單項鏈表連接起來。

typedef struct A_BLOCK_LINK
{
	struct A_BLOCK_LINK *pxNextFreeBlock;	/* 指向下一個空閑塊 */
	size_t xBlockSize;						/* 當前空閑塊的大小*/
} BlockLink_t;

手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理
鏈表的實際結構也考慮了字節(jié)的對齊,這里heapSTRUCT_SIZE的實際大小為8個字節(jié),為了保證字節(jié)對齊,則最小的塊空間heapMINIMUM_BLOCK_SIZE為16。

static const uint16_t heapSTRUCT_SIZE	= ( ( sizeof ( BlockLink_t ) + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK );
#define heapMINIMUM_BLOCK_SIZE	( ( size_t ) ( heapSTRUCT_SIZE * 2 ) )

記錄鏈表的頭和尾

static BlockLink_t xStart, xEnd;

記錄剩余的內(nèi)存堆大小

static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;

2.1 內(nèi)存堆初始化函數(shù)prvHeapInit

與heap_1.c相同,保證實際劃分內(nèi)存的起始地址對齊,此處不再過多贅述。

pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );

xStart指向空閑內(nèi)存堆鏈表首

xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
xStart.xBlockSize = ( size_t ) 0;

xxStart指向空閑內(nèi)存堆鏈表首

xEnd.xBlockSize = configADJUSTED_HEAP_SIZE;
xEnd.pxNextFreeBlock = NULL;

初始化一個大小為configADJUSTED_HEAP_SIZE的內(nèi)存塊,任務所需的內(nèi)存將從該內(nèi)存塊劃分

pxFirstFreeBlock = ( void * ) pucAlignedHeap;
pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE;
pxFirstFreeBlock->pxNextFreeBlock = &xEnd;

初始化完成后內(nèi)存堆如下圖所示:
手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

2.2 內(nèi)存釋放函數(shù)prvInsertBlockIntoFreeList

獲取所釋放內(nèi)存的大小

xBlockSize = pxBlockToInsert->xBlockSize;

所釋放的內(nèi)存塊由小到大排列起來,此處用for來遍歷內(nèi)存塊,將釋放的內(nèi)存塊插入到合適的位置。

for( pxIterator = &xStart; pxIterator->pxNextFreeBlock->xBlockSize < xBlockSize; pxIterator = pxIterator->pxNextFreeBlock )	\
{																				\
	/* There is nothing to do here - just iterate to the correct position. */	\
}		

將內(nèi)存塊插入到空閑內(nèi)存堆中。

pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;					\
pxIterator->pxNextFreeBlock = pxBlockToInsert;

手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

2.3 內(nèi)存分配函數(shù)pvPortMalloc

關閉任務調(diào)度器,防止其他任務打斷內(nèi)存分配。

vTaskSuspendAll();

第一次分配內(nèi)存堆的時候需要調(diào)用內(nèi)存堆初始化函數(shù)。

	if( xHeapHasBeenInitialised == pdFALSE )
	{
		prvHeapInit();
		xHeapHasBeenInitialised = pdTRUE;
	}

實際所申請的內(nèi)存堆大小需要加上頭部結構體以及用于對齊棄去的字節(jié)。

	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 ) );
		}
	}

由于釋放的內(nèi)存塊是由小到大排列的,因此申請內(nèi)存堆的時候從鏈表首開始遍歷,直到找到合適大小的內(nèi)存堆。

		pxPreviousBlock = &xStart;
		pxBlock = xStart.pxNextFreeBlock;
		while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
		{
			pxPreviousBlock = pxBlock;
			pxBlock = pxBlock->pxNextFreeBlock;
		}

找到合適大小的內(nèi)存堆以后,pvReturn記錄所申請內(nèi)存堆的起始地址(由于每個內(nèi)存塊會包含一個頭部結構體,因此地址需要往后偏移heapSTRUCT_SIZE),最后將申請好的內(nèi)存堆從內(nèi)存堆空間中剔除。

			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;

手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理
如果申請的內(nèi)存堆足夠大,則將其劃分成兩個。

			/* 如果所申請的塊足夠大,則將其劃分成兩個 */
				if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
				{
					/* pxNewBlockLink指向多余內(nèi)存塊的起始地址 */
					pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
					
					/*計算多于的內(nèi)存堆大小*/
					pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
					/*重新給申請的內(nèi)存堆大小賦值*/
					pxBlock->xBlockSize = xWantedSize;

					/* 將多余的內(nèi)存堆插入到空閑內(nèi)存堆中*/
					prvInsertBlockIntoFreeList( ( pxNewBlockLink ) );
				}
				/*計算剩余的內(nèi)存堆大小*/
				xFreeBytesRemaining -= pxBlock->xBlockSize;

重新開啟任務調(diào)度器。

xTaskResumeAll();

內(nèi)存申請失敗的鉤子函數(shù)

	#if( configUSE_MALLOC_FAILED_HOOK == 1 )
	{
		if( pvReturn == NULL )
		{
			extern void vApplicationMallocFailedHook( void );
			vApplicationMallocFailedHook();
		}
	}
	#endif

3、heap_3.c

heap_3.c只對malloc和free進行了簡單封裝,保證FreeRTOS使用時是安全的。

void *pvPortMalloc( size_t xWantedSize )
{
	void *pvReturn;

	vTaskSuspendAll();
	{
		pvReturn = malloc( 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 )
{
	if( pv )
	{
		vTaskSuspendAll();
		{
			free( pv );
			traceFREE( pv, 0 );
		}
		( void ) xTaskResumeAll();
	}
}

pvPortMalloc,vPortFree中采用掛起任務調(diào)度、釋放讓任務調(diào)度來保證線程安全。

4、heap_4.c

heap_4.c在heap_2.c的基礎上,其在釋放內(nèi)存的時候增加了相鄰內(nèi)存塊合并算法,減少了碎片的產(chǎn)生。

4.1 內(nèi)存堆初始化函數(shù)prvHeapInit

確保內(nèi)存堆起始地址對齊

uxAddress = ( size_t ) ucHeap;
	/*字節(jié)對齊*/
	if( ( uxAddress & portBYTE_ALIGNMENT_MASK ) != 0 )
	{
		/*確保劃分內(nèi)存堆的起始地址對齊,與heap_1.c heap_2.c相同*/
		uxAddress += ( portBYTE_ALIGNMENT - 1 );
		uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
		/*計算實際的總內(nèi)存堆大小=總的內(nèi)存堆大小-字節(jié)對齊棄去的大小*/
		xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
	}
	/*pucAlignedHeap為實際的起始地址	*/
	pucAlignedHeap = ( uint8_t * ) uxAddress;

pxEnd指向空閑內(nèi)存堆鏈表首,并將其插入到堆空間的尾部

/* pxEnd指向空閑內(nèi)存堆鏈表首,并將其插入到堆空間的尾部 */
/* 同樣進行地址對齊操作 */
uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
uxAddress -= xHeapStructSize;
uxAddress &= ~( ( size_t ) portBYTE_ALIGNMENT_MASK );
pxEnd = ( void * ) uxAddress;
pxEnd->xBlockSize = 0;
pxEnd->pxNextFreeBlock = NULL;

/* 初始化一個內(nèi)存塊來占據(jù)總的堆大小,任務所需的內(nèi)存將從該內(nèi)存塊劃分 */
	pxFirstFreeBlock = ( void * ) pucAlignedHeap;
	pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
	pxFirstFreeBlock->pxNextFreeBlock = pxEnd;

	/* 因為只有一個大的空閑內(nèi)存塊,因剩余的最小內(nèi)存塊大小即為pxFirstFreeBlock->xBlockSize */
	xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
	/*記錄剩余的空閑內(nèi)存塊大小*/
	xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;

初始化xBlockAllocatedBit,用于標記內(nèi)存塊的類型(是否空閑),xBlockAllocatedBit初始化完成后為0x80000000

xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * heapBITS_PER_BYTE ) - 1 );

內(nèi)存堆初始化完成后如下圖:
手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理

4.2 內(nèi)存堆釋放函數(shù)prvInsertBlockIntoFreeList

遍歷空閑內(nèi)存塊鏈表,將釋放的內(nèi)存塊按照地址由低到高排列.

/* Iterate through the list until a block is found that has a higher address
than the block being inserted. */
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
{
	/* Nothing to do here, just iterate to the right position. */
}

判斷內(nèi)存塊地址是否連續(xù),若連續(xù)則合并內(nèi)存塊。

/* 判斷插入的內(nèi)存塊是否與其位置前面的空閑內(nèi)存塊地址連續(xù),若連續(xù)則合并 */
	puc = ( uint8_t * ) pxIterator;
	/*地址連續(xù)*/
	if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
	{
		/*合并內(nèi)存塊*/
		pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
		pxBlockToInsert = pxIterator;
	}
	else
	{
		mtCOVERAGE_TEST_MARKER();
	}

	/* 判斷是否可以與后面內(nèi)存塊合并 */
	puc = ( uint8_t * ) pxBlockToInsert;
	/*如果地址連續(xù)*/
	if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
	{
		if( pxIterator->pxNextFreeBlock != pxEnd )
		{
			/* 如果下一個內(nèi)存塊不是鏈尾,則合并 */
			pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
			pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
		}
		else
		{
			/*如果下一個內(nèi)存塊為pxEnd,則直接指向*/
			pxBlockToInsert->pxNextFreeBlock = pxEnd;
		}
	}
	else
	{
		/*如果地址不連續(xù)就直接指向下一個內(nèi)存塊*/
		pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
	}

注意:釋放內(nèi)存塊的時候要連續(xù)判斷兩次,第一次判斷釋放的內(nèi)存塊是否能與其前面的內(nèi)存塊合并,第二次判斷合并后的內(nèi)存塊是否能與其后面的內(nèi)存塊合并,如果不能則直接用鏈表連接起來,因此也無法避免產(chǎn)生一些內(nèi)存碎片。

/* 如果內(nèi)存塊未能與其前面的內(nèi)存塊合并,同樣直接用鏈表連接起來 */
if( pxIterator != pxBlockToInsert )
{
	pxIterator->pxNextFreeBlock = pxBlockToInsert;
}
else
{
	mtCOVERAGE_TEST_MARKER();
}

4.3 內(nèi)存堆申請函數(shù)pvPortMalloc

關閉任務調(diào)度器,確保線程安全。

vTaskSuspendAll();

如果第一次申請內(nèi)存堆,則需要初始化內(nèi)存堆

	if( pxEnd == NULL )
	{
		prvHeapInit();
	}

xBlockAllocatedBit初始值位0x80000000,先判斷所申請的內(nèi)存是否過大。

if( ( xWantedSize & xBlockAllocatedBit ) == 0 )

與heap_2.c相同,實際申請的內(nèi)存堆大小需要加上頭部結構體,并進行字節(jié)對齊。

if( xWantedSize > 0 )
			{
				/*增加頭部結構體大小*/
				xWantedSize += xHeapStructSize;

				/* 確保字節(jié)對齊. */
				if( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) != 0x00 )
				{
					/* Byte alignment required. */
					xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) );
					configASSERT( ( xWantedSize & portBYTE_ALIGNMENT_MASK ) == 0 );
				}

判斷剩余的內(nèi)存是否足夠(檢查堆溢出)

if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )

從低地址到高地址遍歷整個鏈表,直至找到合適大小的內(nèi)存堆。

			pxPreviousBlock = &xStart;
			pxBlock = xStart.pxNextFreeBlock;
			/*從低地址到高地址遍歷整個鏈表*/
			while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
			{
				pxPreviousBlock = pxBlock;
				pxBlock = pxBlock->pxNextFreeBlock;
			}

如果找到了合適大小的內(nèi)存堆,則記錄空閑內(nèi)存堆的起始地址。

				*如果找到了合適大小的內(nèi)存堆 */
				if( pxBlock != pxEnd )
				{
					/* pvReturn記錄所申請堆空間的起始地址,由于存在頭部結構體,因此地址需要往后偏移xHeapStructSize */
					pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );

					/* 將申請的內(nèi)存堆從總的內(nèi)存堆中剔除 */
					pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;

					/* 如果實際申請到的內(nèi)存堆較大,可以將其劃分成兩個,多余的內(nèi)存堆重新添加至總的空閑內(nèi)存堆中*/
					if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
					{
						/* pxNewBlockLink指向多余的內(nèi)存堆起始地址 */
						pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
						configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );

						/* 計算多余的內(nèi)存堆大小,以及重新給所申請的內(nèi)存堆賦值 */
						pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
						pxBlock->xBlockSize = xWantedSize;

						/* 將多余的內(nèi)存堆重新添加至空閑內(nèi)存堆中 */
						prvInsertBlockIntoFreeList( pxNewBlockLink );
					}

更新剩余空閑內(nèi)存堆大小以及最小空閑內(nèi)存堆大小。

				/*更新剩余的空閑內(nèi)存堆大小,即減去此處申請所劃去的內(nèi)存堆*/
					xFreeBytesRemaining -= pxBlock->xBlockSize;
					/*更新執(zhí)行過程中剩余的最小空閑內(nèi)存堆大小*/
					if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
					{
						xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
					}

申請內(nèi)存堆成功,標記該內(nèi)存堆。該內(nèi)存堆已經(jīng)從總內(nèi)存堆中劃分出,因此將該內(nèi)存堆指向空

				pxBlock->xBlockSize |= xBlockAllocatedBit;
				pxBlock->pxNextFreeBlock = NULL;

重新打開任務調(diào)度器

( void ) xTaskResumeAll();

4.4 內(nèi)存堆釋放函數(shù)vPortFree

函數(shù)vPortFree在prvInsertBlockIntoFreeList的基礎上,進行了再一次的封裝。

每個內(nèi)存塊頭部有一個BlockLink_t結構體,因此puc向前偏移xHeapStructSize,指向結構體頭部

puc -= xHeapStructSize;

判斷需要釋放的內(nèi)存堆是否是有效內(nèi)存堆

if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )

重新標記內(nèi)存堆為空閑內(nèi)存堆

pxLink->xBlockSize &= ~xBlockAllocatedBit;

將內(nèi)存堆重新插入到空閑內(nèi)存堆中

/*關閉任務調(diào)度器,防止釋放內(nèi)存過程被打斷*/
				vTaskSuspendAll();
				{
					/* 更新剩余的空閑內(nèi)存堆大小 */
					xFreeBytesRemaining += pxLink->xBlockSize;
					traceFREE( pv, pxLink->xBlockSize );
					/*將內(nèi)存堆插入到空閑內(nèi)存堆中*/
					prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
				}
				/*重新打開任務調(diào)度器*/
				( void ) xTaskResumeAll();

5、heap_5.c

heap_5.c在heap_4.c的基礎上,實現(xiàn)了跨越多個非相鄰的內(nèi)存區(qū)域來申請內(nèi)存,即可以同時從多種存儲介質(zhì)上申請內(nèi)存。文章來源地址http://www.zghlxwxcb.cn/news/detail-471753.html

到了這里,關于手把手教你FreeRTOS源碼解析(一)——內(nèi)存管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 手把手教你使用Python寫貪吃蛇游戲(pygame,附源碼)

    貪吃蛇游戲是有史以來最受歡迎的街機游戲之一。在這個游戲中,玩家的主要目標是在不撞墻或不撞墻的情況下抓住最大數(shù)量的水果。在學習 Python 或 Pygame 時,可以將創(chuàng)建蛇游戲視為一項挑戰(zhàn)。這是每個新手程序員都應該接受的最好的初學者友好項目之一。學習構建視頻游戲

    2024年02月16日
    瀏覽(23)
  • 手把手教你跑一個UVM_demo(含源碼)(一鍵復現(xiàn))

    手把手教你跑一個UVM_demo(含源碼)(一鍵復現(xiàn))

    筆記內(nèi)容對應張強所著的《UVM實戰(zhàn)》。該書對UVM使用進行了比較詳盡的介紹,并在前言中提供了書籍對應源碼的下載網(wǎng)址,是一本帶有實操性的書籍,對新手比較友好,推薦閱讀。 學習完第二章后,我們對UVM已經(jīng)有了一個基本概念,但如何在自己的電腦上跑一個UVM deme,讓知

    2024年02月09日
    瀏覽(86)
  • 手把手教你在windows下源碼編譯Open3D

    手把手教你在windows下源碼編譯Open3D

    首先不建議windows下源碼編譯,需要用C++的可以直接下載官網(wǎng)編譯好的版本。熟悉vcpkg的,可以把open3d加到vcpkg使用,參考該博客。 cmake =3.20 python =3.6.0 visual studio =2017 Github下載open3d源碼 打開源碼,新建build文件夾 填寫你的VS版本,例vs2022(17),open3d_install_directory使用當前路徑

    2024年02月05日
    瀏覽(24)
  • 手把手教你基于【SpringBoot+MyBatis】實現(xiàn)員工管理系統(tǒng)?【附完整源碼】

    手把手教你基于【SpringBoot+MyBatis】實現(xiàn)員工管理系統(tǒng)?【附完整源碼】

    Hello,你好呀,我是 灰小猿 ,一個超會寫 BUG 的程序猿??! 近期在學習springboot框架相關的內(nèi)容,相比于SSM, SpringBoot最大的特點就是集成了Spring和SpringMVC,讓之前繁瑣的配置工作變得更加簡潔, 同時對于業(yè)務邏輯層的處理也更加的友好, 所以今天就使用 SpringBoot整合MyBati

    2023年04月08日
    瀏覽(79)
  • 手把手教你使用Python實現(xiàn)推箱子小游戲(附完整源碼)

    手把手教你使用Python實現(xiàn)推箱子小游戲(附完整源碼)

    我們這個項目是一個基于Python實現(xiàn)的推箱子小游戲,名叫Sokoban: 這個游戲的目的是讓玩家,也就是大寫的 P ,推著箱子 # ,填充用小寫的 o 標記的地面上的洞 該版本的Sokoban的規(guī)則如下: 游戲在矩形的二維網(wǎng)格上舉行,其 原點(0,0) 位于左上方 網(wǎng)格上的每個單元格可以隨時包

    2024年02月03日
    瀏覽(38)
  • 手把手教你如何在Linux下寫進度條小程序(附源碼)

    手把手教你如何在Linux下寫進度條小程序(附源碼)

    錄屏2023 進入ProgressBar這個目錄之后,使用ls命令查看是否創(chuàng)建成功 編寫makefile文件是為了,使用make命令構建我們的.c文件,生成可執(zhí)行程序 把源碼復制粘貼到我們對應的文件中 使用vim指令先打開頭文件 按小寫i,進入插入模式(Insert),直接拷貝上去 再按esc,直接輸入:wq(

    2024年02月04日
    瀏覽(27)
  • FPGA之手把手教你寫串口協(xié)議解析(STM32與FPGA數(shù)據(jù)互傳)

    最近趁熱打鐵做了一個關于STM32與FPGA通信并且控制高速DA模塊產(chǎn)生不同頻率信號的正弦波、方波、三角波和鋸齒波的項目,從中收獲到了很多東西,也踩了一些雷和坑,將分為幾篇文章將整個過程分享出來。 這一次準備分享的是對串口數(shù)據(jù)的解析和賦值。解析的數(shù)據(jù)由STM32發(fā)

    2024年02月06日
    瀏覽(28)
  • 【換臉詳細教程】手把手教你進行AI換臉:換臉流程及源碼詳解

    【換臉詳細教程】手把手教你進行AI換臉:換臉流程及源碼詳解

    最近AI換臉貌似比較火爆,就稍微研究了一下相關了內(nèi)容。AI換臉是一個娛樂性比較強的應用,這種錯位感讓人覺得非常有趣。很多人都以為這是什么黑科技,但這里想告訴大家,AI換臉其實很簡單。只需要你會一點Python基礎,就可以實現(xiàn)自己的AI換臉程序。 本文將詳細介紹圖

    2024年02月15日
    瀏覽(25)
  • 【Golang項目實戰(zhàn)】手把手教你寫一個備忘錄程序|附源碼——建議收藏

    【Golang項目實戰(zhàn)】手把手教你寫一個備忘錄程序|附源碼——建議收藏

    博主簡介: 努力學習的大一在校計算機專業(yè)學生,熱愛學習和創(chuàng)作。目前在學習和分享:數(shù)據(jù)結構、Go,Java等相關知識。 博主主頁: @是瑤瑤子啦 所屬專欄: Go語言核心編程 近期目標: 寫好專欄的每一篇文章 前幾天瑤瑤子學習了Go語言的基礎語法知識,那么今天我們就寫個

    2024年02月06日
    瀏覽(28)
  • 《手把手教你》系列技巧篇(六)-java+ selenium自動化測試-閱讀selenium源碼(詳細教程)

    《手把手教你》系列技巧篇(六)-java+ selenium自動化測試-閱讀selenium源碼(詳細教程)

    1.簡介 前面幾篇基礎系列文章,足夠你邁進了Selenium門檻,再不濟你也至少知道如何寫你第一個基于Java的Selenium自動化測試腳本。接下來宏哥介紹Selenium技巧篇,主要是介紹一些常用的Selenium方法或者接口(API),通過這些接口(API)或者方法的具體操作,達到能夠熟練使用

    2024年02月03日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包