1.簡介
內存管理:指軟件運行時對計算機內存資源的分配和使用的技術。其最主要的目的是如何高效,快速的分配,并且在適當的時候釋放和回收內存資源。 內存管理的實現方法有很多種,最終都是要實現兩個函數: malloc 和 free。
malloc :函數用于內存申請;
free: 函數用于內存釋放。
1.1 分塊式內存管理原理
由上圖可知,分塊式內存管理由內存池和內存管理表兩部分組成。內存池被等分為 n塊,對應的內存管理表,大小也為 n,內存管理表的每一個項對應內存池的一塊內存。
內存管理表的項值代表的意義:當該項值為 0 的時候,代表對應的內存塊未被占用;當該項值非零的時候,代表該項對應的內存塊已經被占用,其數值則代表被連續占用的內存塊數。
比如:某項值為 10,那么說明包括本項對應的內存塊在內,總共分配了 10 個內存塊給外部的某個指針。內寸分配方向如圖所示,是從頂—>底的分配方向。即首先從最末端開始找空內存。當內存管理剛初始化的時候,內存表全部清零,表示沒有任何內存塊被占用。
1.2 分配原理
當指針 p 調用 malloc 申請內存的時候,先判斷 p 要分配的內存塊數(m),然后從第 n 項開始,向下查找,直到找到 m 塊連續的空內存塊(即對應內存管理表項為 0),然后將這 m 個內存管理表項的值都設置為 m(標記被占用),最后,把最后的這個空內存塊的地址返回指針 p,完成一次分配。
注:如果當內存不夠的時候(找到最后也沒找到連續的 m 塊空閑內存),則返回 NULL 給 p,表示分配失敗。
1.2 釋放原理
當 p 申請的內存用完,需要釋放的時候,調用 free 函數實現。 free 函數先判斷 p 指向的內存地址所對應的內存塊,然后找到對應的內存管理表項目,得到 p 所占用的內存塊數目 m(內存管理表項目的值就是所分配內存塊的數目),將這 m 個內存管理表項目的值都清零,標記釋放,完成一次內存釋放。
2.軟件分析
頭文件:
#ifndef __MALLOC_H
#define __MALLOC_H
#include "stm32f10x.h"
#ifndef NULL
#define NULL 0
#endif
//內存參數設定.
#define MEM_BLOCK_SIZE 32 //內存塊大小為32字節
#define MEM_MAX_SIZE 42*1024 //最大管理內存 42K
#define MEM_ALLOC_TABLE_SIZE MEM_MAX_SIZE/MEM_BLOCK_SIZE //內存表大小
//內存管理控制器
struct _m_mallco_dev
{
void (*init)(void); //初始化
u8 (*perused)(void); //內存使用率
u8 *membase; //內存池
u16 *memmap; //內存管理狀態表
u8 memrdy; //內存管理是否就緒
};
extern struct _m_mallco_dev mallco_dev; //在mallco.c里面定義
void mymemset(void *s,u8 c,u32 count); //設置內存
void mymemcpy(void *des,void *src,u32 n);//復制內存
void mem_init(void); //內存管理初始化函數(外/內部調用)
u32 mem_malloc(u32 size); //內存分配(內部調用)
u8 mem_free(u32 offset); //內存釋放(內部調用)
u8 mem_perused(void); //得內存使用率(外/內部調用)
////////////////////////////////////////////////////////////////////////////////
//用戶調用函數
void myfree(void *ptr); //內存釋放(外部調用)
void *mymalloc(u32 size); //內存分配(外部調用)
void *myrealloc(void *ptr,u32 size); //重新分配內存(外部調用)
#endif
參考例程:
#include "malloc.h"
//內存池(4字節對齊)
__align(4) u8 membase[MEM_MAX_SIZE]; //SRAM內存池
//內存管理表
u16 memmapbase[MEM_ALLOC_TABLE_SIZE]; //SRAM內存池MAP
//內存管理參數
const u32 memtblsize=MEM_ALLOC_TABLE_SIZE; //內存表大小
const u32 memblksize=MEM_BLOCK_SIZE; //內存分塊大小
const u32 memsize=MEM_MAX_SIZE; //內存總大小
//內存管理控制器
struct _m_mallco_dev mallco_dev=
{
mem_init, //內存初始化
mem_perused, //內存使用率
membase, //內存池
memmapbase, //內存管理狀態表
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;
}
//內存管理初始化
void mem_init(void)
{
mymemset(mallco_dev.memmap, 0,memtblsize*2);//內存狀態表數據清零
mymemset(mallco_dev.membase, 0,memsize); //內存池所有數據清零
mallco_dev.memrdy=1; //內存管理初始化OK
}
//獲取內存使用率
//返回值:使用率(0~100)
u8 mem_perused(void)
{
u32 used=0;
u32 i;
for(i=0;i
{
if(mallco_dev.memmap[i])
used++;
}
return (used*100)/(memtblsize);
}
//內存分配(內部調用)
//memx:所屬內存塊
//size:要分配的內存大小(字節)
//返回值:0XFFFFFFFF,代表錯誤;其他,內存偏移地址
u32 mem_malloc(u32 size)
{
signed long offset=0;
u16 nmemb; //需要的內存塊數
u16 cmemb=0;//連續空內存塊數
u32 i;
if(!mallco_dev.memrdy)
mallco_dev.init(); //未初始化,先執行初始化
if(size==0)
return 0XFFFFFFFF; //不需要分配
nmemb=size/memblksize; //獲取需要分配的連續內存塊數
if(size%memblksize)
nmemb++;
for(offset=memtblsize-1;offset>=0;offset--) //搜索整個內存控制區
{
if(!mallco_dev.memmap[offset])
cmemb++; //連續空內存塊數增加
else
cmemb=0; //連續內存塊清零
if(cmemb==nmemb) //找到了連續nmemb個空內存塊
{
for(i=0;i
{
mallco_dev.memmap[offset+i]=nmemb;
}
return (offset*memblksize); //返回偏移地址
}
}
return 0XFFFFFFFF;//未找到符合分配條件的內存塊
}
//釋放內存(內部調用)
//offset:內存地址偏移
//返回值:0,釋放成功;1,釋放失敗;
u8 mem_free(u32 offset)
{
int i;
if(!mallco_dev.memrdy)//未初始化,先執行初始化
{
mallco_dev.init();
return 1;//未初始化
}
if(offset
{
int index=offset/memblksize; //偏移所在內存塊號碼
int nmemb=mallco_dev.memmap[index]; //內存塊數量
for(i=0;i
{
mallco_dev.memmap[index+i]=0;
}
return 0;
}
else
return 2;//偏移超區了.
}
//釋放內存(外部調用)
//ptr:內存首地址
void myfree(void *ptr)
{
u32 offset;
if(ptr==NULL)
return;//地址為0.
offset=(u32)ptr-(u32)mallco_dev.membase;
mem_free(offset); //釋放內存
}
//分配內存(外部調用)
//size:內存大小(字節)
//返回值:分配到的內存首地址.
void *mymalloc(u32 size)
{
u32 offset;
offset=mem_malloc(size);
if(offset==0XFFFFFFFF)
return NULL;
else
return (void*)((u32)mallco_dev.membase+offset);
}
//重新分配內存(外部調用)
//*ptr:舊內存首地址
//size:要分配的內存大小(字節)
//返回值:新分配到的內存首地址.
void *myrealloc(void *ptr,u32 size)
{
u32 offset;
offset=mem_malloc(size);
if(offset==0XFFFFFFFF)
return NULL;
else
{
mymemcpy((void*)((u32)mallco_dev.membase+offset),ptr,size); //拷貝舊內存內容到新內存
myfree(ptr); //釋放舊內存
return (void*)((u32)mallco_dev.membase+offset); //返回新內存首地址
}
}
主函數測試:
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "key.h"
#include "malloc.h"
#include "usmart.h"
int main(void)
{
u8 key;
u8 i=0;
u8 *p=0;
u8 *tp=0;
u8 paddr[18]; //存放P Addr:+p地址的ASCII值
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設置中斷優先級分組2
delay_init(); //延時函數初始化
uart_init(9600); //串口初始化為9600
LED_Init(); //初始化與LED連接的硬件接口
LCD_Init(); //初始化LCD
usmart_dev.init(72); //初始化USMART
KEY_Init(); //按鍵初始化
mem_init(); //初始化內存池
POINT_COLOR=RED;//設置字體為紅色
LCD_ShowString(60,50,200,16,16,"Mini STM32");
LCD_ShowString(60,70,200,16,16,"MALLOC TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2014/3/12");
LCD_ShowString(60,130,200,16,16,"KEY0:Malloc");
LCD_ShowString(60,150,200,16,16,"KEY1:Write Data");
LCD_ShowString(60,170,200,16,16,"WK_UP:Free");
POINT_COLOR=BLUE;//設置字體為藍色
LCD_ShowString(60,190,200,16,16,"SRAM USED: %");
while(1)
{
key=KEY_Scan(0);//不支持連按
switch(key)
{
case 0: //沒有按鍵按下
break;
case 1: //KEY0按下
p=mymalloc(2048); //申請2K字節
if(p!=NULL)
sprintf((char*)p,"Memory Malloc Test%03d",i);//向p寫入一些內容
break;
case 2: //KEY1按下
if(p!=NULL)
{
sprintf((char*)p,"Memory Malloc Test%03d",i);//更新顯示內容
LCD_ShowString(60,250,200,16,16,p); //顯示P的內容
}
break;
case 3: //WK_UP按下
myfree(p); //釋放內存
p=0; //指向空地址
break;
}
if(tp!=p)
{
tp=p;
sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);
LCD_ShowString(60,230,200,16,16,paddr); //顯示p的地址
if(p)LCD_ShowString(60,250,200,16,16,p);//顯示P的內容
else LCD_Fill(60,250,239,266,WHITE); //p=0,清除顯示
}
delay_ms(10);
i++;
if((i%20)==0)//DS0閃爍.
{
LCD_ShowNum(60+80,190,mem_perused(),3,16);//顯示內存使用率
LED0=!LED0;
}
}
}
參考:
1.原子庫函數教程
2.STM32-內存管理
上一篇:STM32學習筆記一一UCOSII(1)
下一篇:STM32學習筆記一一DMA傳輸
推薦閱讀
史海拾趣
隨著全球化進程的加速,Conexcon Group積極實施國際化戰略,將業務拓展至海外市場。公司通過在海外設立研發中心和生產基地,進一步提升了自身的研發能力和生產效率。同時,公司還加強了與國際知名品牌的合作,通過共同推廣和營銷活動,提升了自身品牌在國際市場的知名度和影響力。這些舉措不僅為公司帶來了豐厚的利潤回報,也為公司的長遠發展奠定了堅實的基礎。
隨著全球化進程的加速,Conexcon Group積極實施國際化戰略,將業務拓展至海外市場。公司通過在海外設立研發中心和生產基地,進一步提升了自身的研發能力和生產效率。同時,公司還加強了與國際知名品牌的合作,通過共同推廣和營銷活動,提升了自身品牌在國際市場的知名度和影響力。這些舉措不僅為公司帶來了豐厚的利潤回報,也為公司的長遠發展奠定了堅實的基礎。
Array Microsystems Inc公司自創立之初,便專注于陣列傳感器技術的研發。在成立初期,公司面臨資金短缺和技術瓶頸的雙重挑戰。然而,通過不懈的努力和持續的技術創新,Array Microsystems Inc成功研發出了一款高靈敏度、低功耗的陣列傳感器。這一突破性的技術不僅填補了市場的空白,還為公司帶來了可觀的利潤。隨著產品的推廣和應用,Array Microsystems Inc逐漸在電子行業中嶄露頭角。
ARCOL公司的創立,標志著電子行業中一顆新星的誕生。在公司成立之初,ARCOL就專注于電子元器件的研發和生產。憑借創始團隊深厚的技術背景和敏銳的市場洞察力,公司成功開發出了一系列高質量的電子元器件產品,并很快在市場上贏得了良好的口碑。初步的成功為ARCOL公司的后續發展奠定了堅實的基礎。
隨著時間的推移,Cardinal不斷推出新的產品系列以滿足市場需求。2013年,公司推出了CJ速差器系列,這一系列產品以其高性能和穩定性受到了市場的廣泛好評。隨后,在2015年,CJ系列被移植到2.5×2.0封裝尺寸,進一步提高了產品的集成度和可靠性。這些產品系列的拓展與升級不僅豐富了公司的產品線,也為客戶提供了更多的選擇。
為了進一步提升企業的競爭力和市場份額,振華積極實施國際化戰略。公司加強與國外企業的合作與交流,積極參與國際市場競爭,通過引進外資、設立海外研發機構等方式,不斷拓展海外市場。同時,振華還注重提升產品的國際競爭力,加強與國際標準的對接和認證工作,確保產品能夠滿足不同國家和地區的市場需求。
本帖最后由 paulhyde 于 2014-9-15 02:59 編輯 請教各位電子設計競賽過來人,做控制類的題目需要準備什么樣的模塊?或者傳感器模塊? 謝謝啦。 [ 本帖最后由 open82977352 于 2010-2-2 19:50 編輯 ] … 查看全部問答∨ |
|
我是四川人,目前在上海工作,最近打算回成都找個工作。 簡單介紹一下自己。名校碩士畢業,三年vxworks設備驅動及DSP(TI C6000系列)軟件開發經驗,擅長數字信號處理,數值計算方面的算法開發,數模電路有些基礎,但 ...… 查看全部問答∨ |
1. ; 文件名:TEST4.S 2. ; 功能:通過查表實現程序散轉 3. ; 說明:使用ARMulate軟件仿真調試 4. NumCon EQU 0x40003001 ; 定義變量NumCn ...… 查看全部問答∨ |
這個問題我已google,baidu之。。還是不是很明白。。我剛涉及到驅動開發這一塊內容。。希望能得到大家通俗的解釋。。。不要給我google和baidu出的答案。。… 查看全部問答∨ |
請教: 我們公司想找一家公司開一塊電腦主板! 請各位大哥一些有這方面能力的公司! QQ:19335776 EMAIL:LSH9982008@163.COM 謝謝!… 查看全部問答∨ |
一早找到HT7333代替BL8505,HT7333的1、3腳分別接到C7兩端(其中1腳接到C7的接地端),2腳接到C4的正端(在PCB版上靠近R14),L1不接。 就穩定的提供了3.3V的電壓。 以前講過:1117- ...… 查看全部問答∨ |
NAND01G-B datasheet中怎么沒有命令寄存器,數據寄存器,地址寄存器的內存映射 dsp通過emifa與nandflash連接,nandflash型號是NAND01GW3A. 可是datasheet中怎么沒有命令寄存器,數據寄存器,地址寄存器的內存映射地址.… 查看全部問答∨ |