網站首頁 編程語言 正文
heap_2.c
內存堆管理
heap_2和heap_1一樣是開辟一個大數組作為堆空間供用戶使用,但是采用單項不循環鏈表來管理內存的分配釋放,主要思想是用鏈表把內存塊串起來,數據結構如下
/* 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;
與引入鏈表管理而帶來的相關變量如下
//鏈表結構體對齊后所占字節數 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 ) ) /* Create a couple of list links to mark the start and end of the list. */ //定義2個局部靜態全局結構體變量用于管理 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;
分配
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 ) ); } } //待分配字節數大于0且小于總共堆字節數 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指向頭鏈表 pxPreviousBlock = &xStart; //pxBlock指向第一個開始空閑塊 pxBlock = xStart.pxNextFreeBlock; //當pxBlock所管理的空閑塊字節數小于待分配的 //且沒有遍歷到空閑塊管理鏈表尾部則一直遍歷 while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) ) { //pxPreviousBlock這里是保存當前空閑塊管理結構體,為了后面找到返回的內存地址 pxPreviousBlock = pxBlock; //指向下一個空閑塊管理結構體 pxBlock = pxBlock->pxNextFreeBlock; } /* If we found the end marker then a block of adequate size was not found. */ //pxBlock不等于結尾說明找到符合大小的空閑塊 if( pxBlock != &xEnd ) { /* Return the memory space - jumping over the BlockLink_t structure at its start. */ //pvReturn用作返回給用戶,這里要偏移一個空閑塊管理結構體占用內存大小 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指向的空閑塊被分配了, //所以要把pxPreviousBlock->pxNextFreeBlock指向的空閑塊移除出去, //也就是pxPreviousBlock->pxNextFreeBlock指向pxBlock->pxNextFreeBlock //也就是跳過分配出去的那個塊 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock; /* If the block is larger than required it can be split into two. */ //這里判斷, //如果將要分配出去的內存塊大小xBlockSize比分配出去的還要大heapMINIMUM_BLOCK_SIZE(2倍管理結構體) //為了節約就把再分成2塊,一塊返回給用戶, //一塊構造一個新的空閑管理結構體后插入空閑鏈表 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. */ //注意這里xWantedSize是管理結構體和和真正需要字節數之和 //所以是在pxBlock基礎上偏移xWantedSize作為新的管理結構體 pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize ); /* Calculate the sizes of two blocks split from the single block. */ //pxNewBlockLink新的管理結構體大小 //是待分配pxBlock->xBlockSize-xWantedSize pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize; //更新pxBlock->xBlockSize大小為xWantedSize pxBlock->xBlockSize = xWantedSize; /* Insert the new block into the list of free blocks. */ //把新構造的空閑管理結構體按xBlockSize大小升序插入到空閑鏈表 prvInsertBlockIntoFreeList( ( pxNewBlockLink ) ); } //還??臻e字節數要減去分配出去的 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; }
其中xFreeBytesRemaining
初始化如下
/* Keeps track of the number of free bytes remaining, but says nothing about fragmentation. */ static size_t xFreeBytesRemaining = configADJUSTED_HEAP_SIZE;
初始化內存堆
static void prvHeapInit( void ) { BlockLink_t *pxFirstFreeBlock; uint8_t *pucAlignedHeap; /* Ensure the heap starts on a correctly aligned boundary. */ //與heap1操作相同,確保portBYTE_ALIGNMENT字節對齊,實際使用的首址是pucAlignedHeap 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. */ //空閑鏈表結構體頭部初始化,pxNextFreeBlock指向實際使用的首址pucAlignedHeap xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap; //空閑鏈表結構體頭部沒有可用內存,所以xBlockSize是0 xStart.xBlockSize = ( size_t ) 0; /* xEnd is used to mark the end of the list of free blocks. */ //空閑鏈表結構體尾部初始化,xBlockSize=configADJUSTED_HEAP_SIZE僅僅是為了后面的升序排列,不代表可以空閑字節數 xEnd.xBlockSize = configADJUSTED_HEAP_SIZE; //空閑鏈表結構體尾部初始化,pxNextFreeBlock指向NULL表示結尾 xEnd.pxNextFreeBlock = NULL; /* To start with there is a single free block that is sized to take up the entire heap space. */ //第一個空閑塊,pxFirstFreeBlock,即上面xStart指向的pucAlignedHeap pxFirstFreeBlock = ( void * ) pucAlignedHeap; //可以空閑內存為configADJUSTED_HEAP_SIZE pxFirstFreeBlock->xBlockSize = configADJUSTED_HEAP_SIZE; //指向空閑鏈表結構體尾部 pxFirstFreeBlock->pxNextFreeBlock = &xEnd; }
初始化后的示意圖如下
這里注意xBlockSize是包括管理結構體占用內存大小的(出來xStart和xEnd之外,這2個做排序用)
把新構造的結構體插入空閑鏈表
/* 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. */ #define prvInsertBlockIntoFreeList( pxBlockToInsert ) \ { \ BlockLink_t *pxIterator; \ size_t xBlockSize; \ \ //這里獲得新構造的空閑結構體成員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 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(); } }
還??臻e字節數
size_t xPortGetFreeHeapSize( void ) { return xFreeBytesRemaining; }
適用范圍、特點
適用于需要釋放的場合,且每次申請釋放的內存都是固定大小的,因為釋放時不會合并相鄰空閑內存塊,所以如果每次申請釋放都是隨機的,到最后即使剩余內存大于要想要分配,由于有很多小的內存碎片導致最終分配失敗。
原文鏈接:https://blog.csdn.net/weixin_41572450/article/details/107747895
相關推薦
- 2023-03-23 Android進階CoordinatorLayout協調者布局實現吸頂效果_Android
- 2022-07-18 Element-UI:el-table導出為excel
- 2022-10-16 python?math模塊使用方法介紹_python
- 2024-03-14 AOP切面編程,以及自定義注解實現切面
- 2023-01-15 解讀keras中的正則化(regularization)問題_python
- 2022-09-10 關于pandas.date_range()的用法及說明_python
- 2022-03-14 移動端rem適配(rem怎么適配不同的手機)
- 2022-11-14 JVM中的垃圾回收
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支