新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 基于STM32原子戰(zhàn)艦板內(nèi)存管理源碼

基于STM32原子戰(zhàn)艦板內(nèi)存管理源碼

作者: 時間:2016-12-01 來源:網(wǎng)絡(luò) 收藏
走到今天,已經(jīng)開始涉及到計算機核心一點的東西了---內(nèi)存管理。通過本實驗的學(xué)習(xí),能夠較為深刻體會到“指針是c語言的靈魂”這句話的分量。自然對c語言的能力要求就高很多了。
最近有點亂,但是有關(guān)嵌入式系統(tǒng)的學(xué)習(xí)不曾怠慢過。本文是基于原子老師的c源碼,自己的學(xué)習(xí)的心得,只是對源碼作出自己的學(xué)習(xí)理解,同時也插補了一些涉及到的c語言知識。貼出本文不為別的,一來但愿能有有緣人看到本文,提出指正;二來,為了那些不眠的夜,安慰一下自己。
1, 內(nèi)存管理簡介
內(nèi)存管理,是指軟件運行時對計算機內(nèi)存資源的分配和使用的技術(shù)。其最主要的目的是如何高效,快速的分配,并且在適當(dāng)?shù)臅r候釋放和回收內(nèi)存資源。內(nèi)存管理的實現(xiàn)方法有很多種,他們其實最終都是要實現(xiàn)2個函數(shù):malloc和free;malloc函數(shù)用于內(nèi)存申請,free函數(shù)用于內(nèi)存釋放。
先回顧一下c語言知識:計算機內(nèi)存一般分為靜態(tài)存儲區(qū)用以存儲全局變量或常量和動態(tài)存儲區(qū)用以存儲函數(shù)內(nèi)部變量或形參或函數(shù)運算結(jié)果。malloc()函數(shù)的作用是請求系統(tǒng)在內(nèi)存的動態(tài)存儲區(qū)分配若干個字節(jié)的存儲空間,函數(shù)的返回值是首字節(jié)地址,可見malloc()函數(shù)是指針類型。free(P)的作用是釋放指針變量P所指向的動態(tài)空間。
本章,我們介紹一種比較簡單的辦法來實現(xiàn):分塊式內(nèi)存管理。下面我們介紹一下該方法的實現(xiàn)原理,如圖所示(示意圖):
內(nèi)存塊1 內(nèi)存塊2 內(nèi)存塊3 ……內(nèi)存塊n 內(nèi)存池
| | | |
第1項 第2項 第3項 ……第n項 內(nèi)存管理表
<<-----分配方向
|
malloc,free等函數(shù)
圖解:從上圖可以看出,分塊式內(nèi)存管理由內(nèi)存池和內(nèi)存管理表兩部分組成。內(nèi)存池被等分為n塊,對應(yīng)的內(nèi)存管理表,大小也為n,內(nèi)存管理表的每一個項對應(yīng)內(nèi)存池的一塊內(nèi)存。
內(nèi)存管理表的項值代表的意義為:當(dāng)該項值為0的時候,代表對應(yīng)的內(nèi)存塊未被占用,當(dāng)該項值非零的時候,代表該項對應(yīng)的內(nèi)存塊已經(jīng)被占用,其數(shù)值則代表被連續(xù)占用的內(nèi)存塊數(shù)。比如某項值為10,那么說明包括本項對應(yīng)的內(nèi)存塊在內(nèi),總共分配了10個內(nèi)存塊給外部的某個指針。
內(nèi)寸分配方向如圖所示,是從頂à底的分配方向。即首先從最末端開始找空內(nèi)存。當(dāng)內(nèi)存管理剛初始化的時候,內(nèi)存表全部清零,表示沒有任何內(nèi)存塊被占用。
分配原理:
當(dāng)指針p調(diào)用malloc申請內(nèi)存的時候,先判斷p要分配的內(nèi)存塊數(shù)(m),然后從第n項開始,向下查找,直到找到m塊連續(xù)的空內(nèi)存塊(即對應(yīng)內(nèi)存管理表項為0),然后將這m個內(nèi)存管理表項的值都設(shè)置為m(標(biāo)記被用),最后,把最后的這個空內(nèi)存塊的地址返回指針p,完成一次分配。注意,如果當(dāng)內(nèi)存不夠的時候(找到最后也沒找到連續(xù)的m塊空閑內(nèi)存),則返回NULL(空指針)給p,表示分配失敗。
釋放原理:
當(dāng)p申請的內(nèi)存用完,需要釋放的時候,調(diào)用free函數(shù)實現(xiàn)。free函數(shù)先判斷p指向的內(nèi)存地址所對應(yīng)的內(nèi)存塊,然后找到對應(yīng)的內(nèi)存管理表項目,得到p所占用的內(nèi)存塊數(shù)目m(內(nèi)存管理表項目的值就是所分配內(nèi)存塊的數(shù)目),將這m個內(nèi)存管理表項目的值都清零,標(biāo)記釋放,完成一次內(nèi)存釋放。
關(guān)于分塊式內(nèi)存管理的原理,我們就介紹到這里。
2, 硬件設(shè)計:
本章實驗功能簡介:開機后,顯示提示信息,等待外部輸入。KEY0用于申請內(nèi)存,每次申請2K字節(jié)內(nèi)存。KEY1用于寫數(shù)據(jù)到申請到的內(nèi)存里面。KEY2用于釋放內(nèi)存。WK_UP用于切換操作內(nèi)存區(qū)(內(nèi)部內(nèi)存/外部內(nèi)存)。DS0用于指示程序運行狀態(tài)。本章我們還可以通過USMART調(diào)試,測試內(nèi)存管理函數(shù)。
本實驗用到的硬件資源有:
1) 指示燈DS0
2) 四個按鍵
3) 串口 //USMART
4) TFTLCD模塊
5) IS62WV51216
3, 軟件設(shè)計:
本章,我們將內(nèi)存管理部分單獨做一個分組,在工程目錄下新建一個MALLOC的文件夾,然后新建malloc.c和malloc.h兩個文件,將他們保存在MALLOC文件夾下。
在MDK新建一個MALLOC的組,然后將malloc.c文件加入到該組,并將MALLOC文件夾添加到頭文件包含路徑。
打開malloc.c文件,輸入如下代碼:由于本實驗涉及到的c語言知識,尤其是指針知識較多,所以就邊用邊學(xué)
#include "malloc.h"
//內(nèi)存池(4字節(jié)對齊)
__align(4) u8 mem1base[MEM1_MAX_SIZE]; //內(nèi)部SRAM內(nèi)存池
/*
" u8 mem1base[MEM1_MAX_SIZE];"該數(shù)組是定義拿出內(nèi)部內(nèi)存池的40K的空間來做實驗,為什么該數(shù)組是u8類型?計算機內(nèi)存是以字節(jié)為單位的存儲空間,內(nèi)存中的每個字節(jié)都有唯一的編號,這個編號就叫地址。在這里就是定義40K個元素,每個元素代表一個字節(jié)。整個數(shù)組就代表整個內(nèi)部SRAM內(nèi)存池的總?cè)萘考?0K個元字節(jié)的總空間容量。因為不管是存儲什么數(shù)據(jù)類型內(nèi)存中的地址編號都是32位的,即每個地址編號可以容納4個字節(jié),而不同的數(shù)據(jù)類型存儲在不同的內(nèi)存存儲區(qū),這就是為什么定義變量時一定要先聲明其數(shù)據(jù)類型的原因。存儲一個字符需要一個字節(jié)的存儲空間,存儲一個short類型需要2個字節(jié)的存儲空間,存儲一個int或float需要4個字節(jié)空間,就如同PLC內(nèi)存中的字節(jié),字,雙字的定義規(guī)則一樣(如字節(jié)MB0,MB1,MB0和MB1構(gòu)成MW0;MW0和MW2構(gòu)成32位的雙字DW0,DW4,DW8)。“__align(4)”就是規(guī)定4個字節(jié)對齊,即每個32的地址編號存儲一個數(shù)據(jù)類型?比如,字符存儲區(qū)中地址編號MB0可以存儲一個字節(jié)即8個位的數(shù)據(jù),而存儲MB0這個地址編號是以32位的空間來存儲,也就是說不管是什么類型數(shù)據(jù),存儲它的地址編號都是32的,所以指針值一定是32位的。
//“#define MEM1_MAX_SIZE 40*1024 //最大管理內(nèi)存 40K”,意思是mem1base[MEM1_MAX_SIZE]有40k個元素
*/
__align(4) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));//外部SRAM內(nèi)存池
//#define MEM2_MAX_SIZE 200*1024 //最大管理內(nèi)存200K,意思是mem2base[MEM2_MAX_SIZE]數(shù)組有200K個u8類型元素,第一個元素的地址存儲在 //外部存儲器SRAM的0X68000000地址,
//內(nèi)存管理表
u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE]; //內(nèi)部SRAM內(nèi)存池MAP
/*
//#define MEM1_ALLOC_TABLE_SIZE MEM1_MAX_SIZE/MEM1_BLOCK_SIZE //內(nèi)存表大小,MEM1_MAX_SIZE/MEM1_BLOCK_SIZE==1250
//#define MEM1_BLOCK_SIZE 32 //內(nèi)存塊大小為32字節(jié);“MEM1_MAX_SIZE/MEM1_BLOCK_SIZE ”的含義是內(nèi)部SRAM內(nèi)存池總共40K字節(jié)的容量除以32個字節(jié),得到一共40K/32==1250個內(nèi)存塊;也就是說將內(nèi)部SRAM內(nèi)存池劃為1250個內(nèi)存塊。
“u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];”實際上就等于“u16 mem1mapbase[1250];”意思是定義一個有1250個內(nèi)存塊(元素)的數(shù)組,每個元素是u16類型數(shù)據(jù);數(shù)組名“mem1mapbase”就是mem1mapbase[0](該數(shù)組的第一個元素它代表1250個內(nèi)存塊中的第一個內(nèi)存塊)的地址,也可以說是指針常量;結(jié)合與之關(guān)聯(lián)的結(jié)構(gòu)體成員“u16 *memmap[2]={ mem1mapbase,mem2mapbase}”指針類型數(shù)組;在這里“mem2mapbase”是外部內(nèi)存的第一個內(nèi)存塊的地址,是個指針常量用以存放u16類型數(shù)據(jù)的地址值;結(jié)合
“mymemset(mallco_dev.memmap[0], 0,memtblsize[0]*2);”函數(shù)分析:結(jié)合本程序和結(jié)構(gòu)體有關(guān)定義“u16 *memmap[2]; ”,首元素memmap[0]=mem1mapbase;也就是說“mallco_dev.memmap[0]”在這里表示1250個內(nèi)部內(nèi)存塊中第一個內(nèi)存塊的地址,根據(jù)“u16 *memmap[2]={ mem1mapbase,mem2mapbase}”推斷出“mallco_dev.memmap[0]”是u16類型指針;
“memtblsize[0]”是什么意思呢?根據(jù)“const u32 memtblsize[2]={1250,6250};”可以得知memtblsize[0]==1250即內(nèi)部內(nèi)存一共有1250個管理項,
void mymemset(void *s,u8 c,u32 count)
{
u8 *xs = s;
while(count--)*xs++=c;
} //把u8類型數(shù)據(jù)c填充到以指針變量s為首地址的內(nèi)存空間中,填充多少個數(shù)由count值決定
該函數(shù)的意思是把u8類型的數(shù)據(jù)“c”填充到u16類型指針元素memmap[0]中(根據(jù)結(jié)構(gòu)體定義“u16 *memmap[2]; ”,而memmap[0]=mem1mapbase),說白了就是把u8類型的數(shù)據(jù)“c”填充到1250個內(nèi)存塊中的count個內(nèi)存塊中。
而mallco_dev.memmap[memx]是16位的,為了將其全部清零,所以乘以2.
本例中,用到了指針類型數(shù)組“u16 *memmap[2]={ mem1mapbase,mem2mapbase}”,為什么要定義指針類型數(shù)組呢?mem1mapbase是數(shù)組
“u16 mem1mapbase[1250];”的首個元素地址(即*mem1mapbase等價于mem1mapbase[0]),而mem1mapbase[0]就代表內(nèi)部存儲器1250個存儲塊中的第一個存儲塊;根據(jù)結(jié)構(gòu)體賦值定義可知:memmap[0]=mem1mapbase。所以mem1mapbase就是“mallco_dev.memmap[0]”,即mem1mapbase是函數(shù)mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2)的第一個形參,因為*mem1mapbase等價于mem1mapbase[0]),而mem1mapbase[0]就代表內(nèi)部存儲器1250個存儲塊中的第一個存儲塊。結(jié)合
void mymemset(void *s,u8 c,u32 count)函數(shù)分析, mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2)函數(shù)的意思是:把0寫入到1250個存儲塊中的第一個存儲塊中;這樣就將一個存儲塊的值賦值為0了。
推斷出“mallco_dev.memmap[0]”是u16類型指針;
;
*/
u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000+MEM2_MAX_SIZE)));
/*
“#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_BLOCK_SIZE”
“#define MEM2_BLOCK_SIZE 32”
外部SRAM內(nèi)存池MAP,同理,“MEM2_MAX_SIZE/MEM2_BLOCK_SIZE”的含義是外部SRAM內(nèi)存池總共200K字節(jié)的容量除以32個字節(jié),得到一共200K/32==6250個內(nèi)存塊;也就是說將外部SRAM內(nèi)存池劃為6250個內(nèi)存塊。
*/
//內(nèi)存管理參數(shù)
/*
內(nèi)存管理表“MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE”分別是1250和6250個“項”.
每個內(nèi)存分塊大小即內(nèi)部和外部SRAM每個內(nèi)存塊占有32個字節(jié)空間“MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE”分別是32個字節(jié);
內(nèi)存總大小“MEM1_MAX_SIZE,MEM2_MAX_SIZE”,分別是40K和200K個字節(jié)的總?cè)萘靠臻g
mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);
*/
const u32 memtblsize[2]={MEM1_ALLOC_TABLE_SIZE,MEM2_ALLOC_TABLE_SIZE};//內(nèi)存管理表大小
const u32 memblksize[2]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE}; //內(nèi)存分塊大小
const u32 memsize[2]={MEM1_MAX_SIZE,MEM2_MAX_SIZE}; //內(nèi)存總大小
/*
struct _m_mallco_dev //內(nèi)存管理控制器,定義一個結(jié)構(gòu)體類型數(shù)據(jù),或結(jié)構(gòu)體變量,
{
void (*init)(u8); //初始化
u8 (*perused)(u8); //內(nèi)存使用率
u8 *membase[2]; //內(nèi)存池 管理2個區(qū)域的內(nèi)存 mem1base,mem2base內(nèi)存池
u16 *memmap[2]; //內(nèi)存管理狀態(tài)表 mem1mapbase(==1250塊),mem2mapbase(6250), //內(nèi)存管理狀態(tài)表
u8 memrdy[2]; //內(nèi)存管理是否就緒
};
1,結(jié)構(gòu)體成員“void (*init)(u8);”是定義了一個指向函數(shù)的指針變量,該指針變量名是init;void表示該函數(shù)沒有返回值(函數(shù)的數(shù)據(jù)類型由返回值決定);u8是函數(shù)的形參。指向函數(shù)的指針變量格式:數(shù)據(jù)類型 + (*變量名)(形參)
本例中:
void mem_init(u8 memx)
{
mymemset(mallco_dev.memmap[memx], 0,memtblsize[memx]*2);//內(nèi)存狀態(tài)表數(shù)據(jù)清零 memx:所屬內(nèi)存塊,即幾號內(nèi)存塊
mymemset(mallco_dev.membase[memx], 0,memsize[memx]); //內(nèi)存池所有數(shù)據(jù)清零
mallco_dev.memrdy[memx]=1;//內(nèi)存管理初始化OK
}
也就是說,本例中用指向函數(shù)的指針變量來表示函數(shù)。c語言規(guī)定函數(shù)名就是函數(shù)的入口地址,也就是說函數(shù)名也是一個指針,指向函數(shù)的入口,根據(jù)這個原理,可以將指向函數(shù)的指針作為函數(shù)的參數(shù)調(diào)用,可以在不同的情況調(diào)用不同的函數(shù);如果一個指向函數(shù)的指針變量等于函數(shù)名就可以說該指向函數(shù)的指針變量指向了該函數(shù),那么指針變量與函數(shù)就是一樣的了。比如:“mem_init(memx);”就等同于“mallco_dev.init(memx);”
2,指針類型數(shù)組“u8 *membase[2];”,意思是該指針類型數(shù)組有兩個“char *”類型的指針元素或者說有兩個“u8 *”類型指針元素;為什么要定義“u8 *”類型呢?因為內(nèi)存存儲區(qū)是根據(jù)數(shù)據(jù)類型來劃分的,如果不明確聲明類型就亂套了。
在C語言和C++語言中,數(shù)組元素全為指針的數(shù)組稱為指針數(shù)組。一維指針數(shù)組的定義形式為:“類型名 *數(shù)組標(biāo)識符[數(shù)組長度]”。
例如,一個一維指針數(shù)組的定義:int *ptr_array[10]。該指針數(shù)組有10個元素,每個元素都是int類型的指針即“int *”類型;
指針類型數(shù)組“u8 *membase[2];”的賦值是mem1base,mem2base, “mem1base,mem2base”分別是內(nèi)部內(nèi)存池和外部內(nèi)存池的數(shù)組名,是指針常量即首元素的地址;因為事先已經(jīng)定義“u8 mem1base[MEM1_MAX_SIZE]”即“u8 mem1base[40K];”。
*/
//內(nèi)存管理控制器,結(jié)構(gòu)體變量賦值,即初始化
struct _m_mallco_dev mallco_dev=
{
mem_init, //內(nèi)存初始化,將函數(shù)名“mem_init”賦給結(jié)構(gòu)體成員“void (*init)(u8);”即指向函數(shù)的指針變量,
mem_perused, //內(nèi)存使用率
mem1base,mem2base, //內(nèi)存池
mem1mapbase,mem2mapbase, //內(nèi)存管理狀態(tài)表,mem1mapbase(1250項),mem2mapbase(6250項)
0,0, //內(nèi)存管理未就緒
};
/*
1,“void *des”無類型指針,不能指向具體的數(shù)據(jù),“void *des”無類型指針指向內(nèi)存中的數(shù)據(jù)類型由用戶自己確定,如malloc()函數(shù)的返回值就是“void *des”無類型指針,因為malloc()函數(shù)的返回值是不確定的是根據(jù)形參的數(shù)據(jù)類型確定的
2,“void mymemcpy(void *des,void *src,u32 n) ”函數(shù)的理解:
des是指針,但是不確定指向什么類型的數(shù)據(jù),換句話說des指針存儲的什么類型數(shù)據(jù)不確定,“u8 *xdes=des;”將des指針存儲的數(shù)據(jù)
存儲到一個新的“u8 *”類型指針xdes中;“u8 *xsrc=src;”同理。
“*xdes++=*xsrc++; ”,當(dāng)*xsrc++(自增)時,即指針“src”指針自增,也就是說把“src”指針逐位復(fù)制到des目標(biāo)指針去了。復(fù)制個數(shù)就是n。
3,“*P”的意義:a),“*P”就是以指針變量P的內(nèi)容(P的內(nèi)容就是指針變量P里存儲的某一類型數(shù)據(jù)的指針值)為地址的變量;b),指針運算符“*”如果是在定義變量時候加在前面,意思是這個變量是指針變量,如 char *a;如果是在訪問指針變量的時候加在前面(如*a),意思是取指針變量指向的值,如 char b=*a; 上面定義了a是一個字符指針,這里就是把指針變量a指向的值取出來并賦給b。
上一頁 1 2 3 下一頁

評論


技術(shù)專區(qū)

關(guān)閉