參考原文《STM32F1開發指南》

內存管理簡介

內存管理,是指軟件運行時對計算機內存資源的分配和使用的技術。最主要的目的是如何高效、快速的分配,並且在適當的時候釋放和回收內存資源。內存管理的實現方法有很多種,但最終是要實現2個函數:malloc(內存申請)和free(內存釋放)。

STM32 原子開發板採用的實現方法是:分塊式內存管理。

分塊式內存管理

stm32 memory 0地址_內存管理

分塊式內存管理由內存池內存管理表兩部分組成。內存池和對應的內存表都分成n塊,是相互對應的。

內存管理表的項值代表的意義為:當該項值為0的時候,代表對應的內存塊未被佔用。當該項值非零的時候,代表 該項對應的內存塊已經被佔用,其數值代表連續佔用的內存塊數。比如某項值為10,説明包含本項在內的內存塊總共佔用了10個分配給外部的某個指針。

內存分配方向如圖所示,是從頂-->底的方向分配的。首先從頂端開始找空內存;當內存管理初始化的時候,內存表全部清零,表示沒有任何內存塊被佔用。

分配原理

當指針 p 調用 malloc 申請內存的時候,先判斷 p 要分配的內存塊數m,然後從第 n 項開始,向下查找,直到找到m塊連續的空內存塊(即對應內存管理表項為0),然後將這 m 個內存管理表項的值都設置為 m(標記被佔用),最後把這個空內存塊的地址返回給指針p,完成一次分配。注意,當內存不夠的時候,則返回 NULLp,表示分配失敗。

釋放原理

當申請的內存用完,需要釋放的時候,調用free函數實現。 free 函數先判斷 p 指向的內存地址所對應的內存塊,然後找到對應的內存管理表項目,得到 p 所佔用的內存塊數目m(內存管理表項目的值就是所分配內存塊的數目),將這m個內存管理表項目的值都清零,標記釋放完成。

源碼

//內存池(32字節對齊)
__align(32) u8 mem1base[MEM1_MAX_SIZE];											//內部SRAM內存池
__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));					//外部SRAM內存池

//內存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];											//內部SRAM內存池MAP
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));	//外部SRAM內存池MAP

//內存管理參數	   
const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};			//內存表大小
const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE};						//內存分塊大小
const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE};								//內存總大小


//內存管理控制器
struct _m_mallco_dev mallco_dev=
{
	my_mem_init,				//內存初始化
	my_mem_perused,				//內存使用率
	mem1base,mem2base,			//內存池
	mem1mapbase,mem2mapbase,	//內存管理狀態表
	0,0,  		 				//內存管理未就緒
};

//複製內存
//*des:目的地址
//*src:源地址
//n:需要複製的內存長度(字節為單位)
void mymemcpy(void *des,void *src,u32 n)  
{  
    u8 *xdes=des;
    u8 *xsrc=src; 
    while(n--)*xdes++=*xsrc++;  
}  

//設置內存
//*s:內存首地址
//c :要設置的值
//count:需要設置的內存大小(字節為單位)
void mymemset(void *s,u8 c,u32 count)  
{  
    u8 *xs = s;  
    while(count--)*xs++=c;  
}	   
//內存管理初始化  
//memx:所屬內存塊
void my_mem_init(u8 memx)  
{  
		mymemset(mallco_dev.memmap[memx], 0, memtblsize[memx]*2);//內存狀態表數據清零  
		mymemset(mallco_dev.membase[memx], 0,memsize[memx]);	//內存池所有數據清零  
		mallco_dev.memrdy[memx]=1;								//內存管理初始化OK  
}  
//獲取內存使用率
//memx:所屬內存塊
//返回值:使用率(0~100)
u8 my_mem_perused(u8 memx)  
{  
    u32 used=0;  
    u32 i;  
    for(i=0;i<memtblsize[memx];i++)  
    {  
        if(mallco_dev.memmap[memx][i])used++; 
    } 
    return (used*100)/(memtblsize[memx]);  
}  
//內存分配(內部調用)
//memx:所屬內存塊
//size:要分配的內存大小(字節)
//返回值:0XFFFFFFFF,代表錯誤;其他,內存偏移地址 
u32 my_mem_malloc(u8 memx,u32 size)  
{  
    signed long offset=0;  
    u32 nmemb;	//需要的內存塊數  
    u32 cmemb=0;    //連續空內存塊數
    u32 i;  
    if(!mallco_dev.memrdy[memx])mallco_dev.init(memx);//未初始化,先執行初始化 
    if(size==0)return 0XFFFFFFFF;//不需要分配
    nmemb=size/memblksize[memx];  	//獲取需要分配的連續內存塊數
    if(size%memblksize[memx])nmemb++;  
    for(offset=memtblsize[memx]-1;offset>=0;offset--)//搜索整個內存控制區  
    {     
        if(!mallco_dev.memmap[memx][offset])cmemb++;//連續空內存塊數增加
        else cmemb=0;								//連續內存塊清零
	
        if(cmemb==nmemb)						//找到了連續nmemb個空內存塊
	{
            for(i=0;i<nmemb;i++)  					//標註內存塊非空 
            {  
                mallco_dev.memmap[memx][offset+i]=nmemb;  
            }  
            return (offset*memblksize[memx]);//返回偏移地址  
	}
    }  

    return 0XFFFFFFFF;//未找到符合分配條件的內存塊  
}  
//釋放內存(內部調用) 
//memx:所屬內存塊
//offset:內存地址偏移
//返回值:0,釋放成功;1,釋放失敗;  
u8 my_mem_free(u8 memx,u32 offset)  
{  
    int i;  
    if(!mallco_dev.memrdy[memx])//未初始化,先執行初始化
    {
	mallco_dev.init(memx);    
        return 1;//未初始化  
    }  
		
    if(offset<memsize[memx])//偏移在內存池內. 
    {  
        int index=offset/memblksize[memx];			//偏移所在內存塊號碼  
        int nmemb=mallco_dev.memmap[memx][index];	//內存塊數量
        for(i=0;i<nmemb;i++)  						//內存塊清零
        {  
            mallco_dev.memmap[memx][index+i]=0;  
        }  
        return 0;  
    }else return 2;//偏移超區了.  
}  


//釋放內存(外部調用) 
//memx:所屬內存塊
//ptr:內存首地址 
void myfree(u8 memx,void *ptr)  
{  
    u32 offset;   
    if(ptr==NULL)return;//地址為0.  
    offset=(u32)ptr-(u32)mallco_dev.membase[memx]; // 計算當前指針在內存塊中的偏移地址
    my_mem_free(memx,offset);	//釋放內存      
}  


//分配內存(外部調用)
//memx:所屬內存塊
//size:內存大小(字節)
//返回值:分配到的內存首地址.
void *mymalloc(u8 memx,u32 size)  
{  
    u32 offset;   
    offset=my_mem_malloc(memx,size);  	   	 	   
    if(offset==0XFFFFFFFF)return NULL;  
    else return (void*)((u32)mallco_dev.membase[memx]+offset);  
}  


//重新分配內存(外部調用)
//memx:所屬內存塊
//*ptr:舊內存首地址
//size:要分配的內存大小(字節)
//返回值:新分配到的內存首地址.
void *myrealloc(u8 memx,void *ptr,u32 size)  
{  
    u32 offset;    
    offset=my_mem_malloc(memx,size);   	
    if(offset==0XFFFFFFFF)return NULL;     
    else  
    {  									   
        mymemcpy((void*)((u32)mallco_dev.membase[memx]+offset),ptr,size);	//拷貝舊內存內容到新內存   
        myfree(memx,ptr);  											  		//釋放舊內存
        return (void*)((u32)mallco_dev.membase[memx]+offset);  				//返回新內存首地址
    }  
}

通過內存管理控制器 malloc_dev 結構體實現對兩個內存池的管理控制。

//內存管理控制器
struct _m_mallco_dev
{
	void (*init)(u8);					//初始化
	u8 (*perused)(u8);		  	    	//內存使用率
	u8 	*membase[SRAMBANK];				//內存池 管理SRAMBANK個區域的內存
	u16 *memmap[SRAMBANK]; 				//內存管理狀態表
	u8  memrdy[SRAMBANK]; 				//內存管理是否就緒
};
extern struct _m_mallco_dev mallco_dev;	 //在mallco.c裏面定義


//內存管理控制器
struct _m_mallco_dev mallco_dev=
{
	my_mem_init,				//內存初始化
	my_mem_perused,				//內存使用率
	mem1base,mem2base,			//內存池
	mem1mapbase,mem2mapbase,	//內存管理狀態表
	0,0,  		 				//內存管理未就緒
};

以下是內存池和內存管理表的定義:

//定義兩個內存池
#define SRAMIN	 0		//內部內存池
#define SRAMEX   1		//外部內存池 

#define SRAMBANK 	2	//定義支持的SRAM塊數.	


//mem1內存參數設定.mem1完全處於內部SRAM裏面.
#define MEM1_BLOCK_SIZE			32  	  						//內存塊大小為32字節
#define MEM1_MAX_SIZE			40*1024  						//最大管理內存 40K
#define MEM1_ALLOC_TABLE_SIZE	MEM1_MAX_SIZE/MEM1_BLOCK_SIZE 	//內存表大小

//mem2內存參數設定.mem2的內存池處於外部SRAM裏面
#define MEM2_BLOCK_SIZE			32  	  						//內存塊大小為32字節
#define MEM2_MAX_SIZE			960 *1024  						//最大管理內存960K
#define MEM2_ALLOC_TABLE_SIZE	MEM2_MAX_SIZE/MEM2_BLOCK_SIZE 	//內存表大小

定義了兩個內存池,一個是內部內存池,一個是外部內存池,在 mallo.c 文件中有對內存池的劃分:

//內存池(32字節對齊)
__align(32) u8 mem1base[MEM1_MAX_SIZE];													//內部SRAM內存池
__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));					//外部SRAM內存池
//內存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];													//內部SRAM內存池MAP
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));	//外部SRAM內存池MAP
//內存管理參數	   
const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};			//內存表大小
const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE};						//內存分塊大小
const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE};								//內存總大小

疑問外部內存池指定地址是 0X68000000,是從哪裏得出來的這個地址?
內部內存則由編譯器自動分配。

此部分代碼的核心函數為 mem_mallocmem_free,這兩個函數只是內部調用,外部調用則使用 mymallocmyfree 兩個函數。

我的筆記###

必須要弄懂 malloc_dev 的定義,它在程序內部使用,對用户是透明的,必須要知道它管理這幾塊內存池,及其對應的管理表,弄懂 my_mem_mallocmy_mem_free 函數,這兩個函數都是內部使用的。

stm32 memory 0地址_內存管理_02

stm32 memory 0地址_內存管理_03