前言:
在进行MTK开发的时候经常需要动态申请内存。我们在开发windows程序时一般都是在需要申请内存的时候去调用malloc(C)或者new(C++),这样申请的内存在堆上,直到我们调用free(C)或者delete(C++)时才释放。但是MTK不像windows那样有很好的内存管理算法,特别是非智能机上亦没有地址转换单元芯片。在MTK如果上我们的应用想使用一块内存,可以采用的办法有:1、申请一个巨大的全局数组g_app[MAX]。这样的做的特点是简单,但是不当。因为应用会一直占据它,即便是在应用退出的情况下。我们的理想环境是应用可以使用自己的一块独立的内存,当应用退出的时候这块内存又可以重新分配给别的应用使用。为了解决1的问题出现了方法:2、从ASM或者Media上分配内存。插:ASM或者Media是什么? 实际上就是系统在初始化的时候的分配的两个全局数组。MTK用它自己的方法去管理这两个数组:a、当有应用需要分配内存的时候从数组中分配一段出去。b、应用释放内存的时候“归还”给数组。释放的时候特别重要的是与前后的free块merge在一起,以形成更大的块。ASM的特点应用进入时申请,退出时释放,期间具有独占性。Media则是所有应用共享的。ctrl_buf算法和前面两个不同,ctrl_buf最大只能分配2K(默认),申请超过2k应用独占内存用ASM比较好。
内存管理算法的特点是要高效、碎片少。
正文:
我做的内存管理示例从应用角度讲类似于Media。 分成4个文件:stdafx.h media_config.h media_simu_pool.cpp main.cpp.
下面我将贴示例代码(代码中有注释),在最后将强调该示例的特点及缺陷。
media_config.h #ifndef __ADM_CONFIG_H__ #define __ADM_CONFIG_H__ #pragma once #include <windows.h> typedef struct _tag_ASM_BUFF_HDR { BOOL bUsed; unsigned long chunksize; struct _tag_ASM_BUFF_HDR* pPre; struct _tag_ASM_BUFF_HDR* pNext; }ASM_BUFF_HDR; #define ASM_BUFF_HDR_LEN (((sizeof(ASM_BUFF_HDR)+3) >> 2) << 2) #define ASM_POOL_SIZE (600 * 1024) #define MIN_CHUNK_SIZE 32 #endif
media_simu_pool.cpp #include "adm_config.h" #include <afxcom_.h> #include <windows.h> /************************************************************************/ /* 每次分配从头开始匹配 从首次适应项分配内存 */ /************************************************************************/ static char* _gp_asm_MemoryPool; static ASM_BUFF_HDR* _gp_cur_mem = NULL; void asm_pool_init_1() { _gp_asm_MemoryPool = (char*)malloc(ASM_POOL_SIZE); ASSERT(_gp_asm_MemoryPool); ASM_BUFF_HDR* phead = (ASM_BUFF_HDR*)_gp_asm_MemoryPool; phead->bUsed = FALSE; phead->chunksize = ASM_POOL_SIZE - ASM_BUFF_HDR_LEN; phead->pPre = phead; phead->pNext = phead; } void asm_mem_alloc_1(void** buff_p, unsigned long& chunk_size)/*指向内存的指针、返回实际分配的字节数*/ { chunk_size = ((chunk_size + 3) >> 2) << 2;/*lign 4*/ chunk_size = chunk_size < MIN_CHUNK_SIZE ? MIN_CHUNK_SIZE : chunk_size;/*最小分配MIN_CHUNK_SIZE*/ if (!_gp_cur_mem){_gp_cur_mem = (ASM_BUFF_HDR*)_gp_asm_MemoryPool;} ASM_BUFF_HDR* pCur = _gp_cur_mem; do{ if(pCur->bUsed){ pCur = pCur->pNext; continue;} if (pCur->chunksize >= chunk_size) { //剩余字节 unsigned long nleft_size = pCur->chunksize - chunk_size; if (nleft_size > ASM_BUFF_HDR_LEN + MIN_CHUNK_SIZE)/*剩余字节足够分配一个新的节点*/ { /*在剩余字节建立一个新的节点*/ ASM_BUFF_HDR* pNew = (ASM_BUFF_HDR*)((unsigned char*)pCur + ASM_BUFF_HDR_LEN + chunk_size); pNew->bUsed = FALSE; pNew->chunksize = pCur->chunksize - ASM_BUFF_HDR_LEN - chunk_size; /*拉链*/ (pCur->pNext)->pPre = pNew; pNew->pNext = pCur->pNext; pCur->pNext = pNew; pNew->pPre = pCur; }else/*剩余字节不足够分配一个新的节点,就将当前结点整个分配出去*/ { chunk_size = pCur->chunksize; } *buff_p = (unsigned char*)((ASM_BUFF_HDR*)pCur + 1); pCur->bUsed = TRUE; pCur->chunksize = chunk_size;/*实际分配的内存是从pCur结点分配出去的所以pCur的chunksize和分配出去的字节数是一样的*/ return; } pCur = pCur->pNext; } while(pCur && (pCur != _gp_cur_mem)); /*分配失败*/ *buff_p = NULL; chunk_size = 0; } BOOL asm_mem_free_1(void* buff_p) { ASSERT(buff_p); if ((unsigned long)buff_p < (unsigned long)_gp_asm_MemoryPool || (unsigned long)buff_p > ((unsigned long) _gp_asm_MemoryPool + ASM_POOL_SIZE)) { return FALSE; } ASM_BUFF_HDR* pf = (ASM_BUFF_HDR*)buff_p - 1; pf->bUsed = FALSE; _gp_cur_mem = pf; ASM_BUFF_HDR* pPre = pf->pPre; ASM_BUFF_HDR* pNext = pf->pNext; /************************************************************************/ /* 因为是双向循环链表头结点和尾结点是互指向的。应该避免这种情况的合并,除非它们的物理地址是相邻的*/ /************************************************************************/ if (!pPre->bUsed) { unsigned char* pPre_end = (unsigned char*)pPre + ASM_BUFF_HDR_LEN + pPre->chunksize; if (pPre_end == (unsigned char*)pf)/*物理地址是相邻的*/ { /*跟前面的相邻块合并*/ pPre->pNext = pNext; pNext->pPre = pPre; pPre->chunksize += (ASM_BUFF_HDR_LEN + pf->chunksize); _gp_cur_mem = pPre; } }else if (!pNext->bUsed) { unsigned char* pCur_end = (unsigned char*)pf + ASM_BUFF_HDR_LEN + pf->chunksize; if (pCur_end == (unsigned char*)pNext) { /*跟后面的相邻块合并*/ ASM_BUFF_HDR* pt = pNext->pNext; pf->pNext = pt; pt->pPre = pf; } }else { /*不能合并仅改标志即可*/ } return TRUE; }
stdafx.h #ifndef __STDFX_H__ #define __STDFX_H__ #pragma once //#ifndef __cplusplus //extern "C" { //#endif extern asm_mem_free_1(void* buff_p); extern void asm_pool_init_1(); extern void asm_mem_alloc_1(void** buff_p, unsigned long& chunk_size); //#ifndef __cplusplus //} //#endif #endif
main.cpp #include "stdfx.h" void main() { asm_pool_init_1(); void* block[10000]; int res_sz = 48; for (int i = 0; i < 16 * 600; ++i) { asm_mem_alloc_1(&block[i], (unsigned long&)res_sz); } for (i = 0; i < 16 * 600; ++i) { asm_mem_free_1(block[i]); } }
算法简述:
1、一开始申请一大块内存,后面应用需要申请的内存就从这块内存中分
2、分配的每一块内存都有一个ASM_BUFF_HDR的头,ASM_BUFF_HDR后面紧跟着的就是分配给用户使用的buff。
3、ASM_BUFF_HDR之间用双向循环链表联系
附图:
测试方法:将ASM_POOL_SIZE设置为192byte, res_sz任然是48byte加上ASM_BUFF_HDR是16byte,这样每一个Node是64byte。192byte刚好可以分配3个node。读者在调试看看各个node的address。
不足:该示例分配的时候需要遍历查找,且是首次适应即分配,并不是按最适合的大小分配。
