日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

正点原子的内存管理_正点原子【STM32-F407探索者】第四十二章 内存管理实验

發布時間:2023/12/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 正点原子的内存管理_正点原子【STM32-F407探索者】第四十二章 内存管理实验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1)資料下載:點擊資料即可下載

2)對正點原子Linux感興趣的同學可以加群討論:935446741

3)關注正點原子公眾號,獲取最新資料更新

上一章,我們學會了使用 STM32F4 驅動外部 SRAM,以擴展 STM32F4 的內存,加上 STM32F4 本身自帶的 192K 字節內存,我們可供使用的內存還是比較多的。如果我們所用的內 存都像上一節的 testsram 那樣,定義一個數組來使用,顯然不是一個好辦法。 本章,我們將學習內存管理,實現對內存的動態管理。本章分為如下幾個部分:

42.1 內存管理簡介

42.2 硬件設計

42.3 軟件設計

42.4 下載驗證

42.1 內存管理簡介

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

本章,我們介紹一種比較簡單的辦法來實現:分塊式內存管理。下面我們介紹一下該方法 的實現原理,如圖 42.1.1 所示:圖 42.1.1 分塊式內存管理原理

從上圖可以看出,分塊式內存管理由內存池和內存管理表兩部分組成。內存池被等分為 n 塊,對應的內存管理表,大小也為 n,內存管理表的每一個項對應內存池的一塊內存。

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

內寸分配方向如圖所示,是從頂→底的分配方向。即首先從最末端開始找空內存。當內存 管理剛初始化的時候,內存表全部清零,表示沒有任何內存塊被占用。

分配原理

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

釋放原理

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

關于分塊式內存管理的原理,我們就介紹到這里。

42.2 硬件設計

本章實驗功能簡介:開機后,顯示提示信息,等待外部輸入。KEY0 用于申請內存,每次 申請 2K 字節內存。KEY1 用于寫數據到申請到的內存里面。KEY2 用于釋放內存。KEY_UP 用于切換操作內存區(內部 SRAM 內存/外部 SRAM 內存/內部 CCM 內存)。DS0 用于指示程 序運行狀態。本章我們還可以通過 USMART 調試,測試內存管理函數。

本實驗用到的硬件資源有:

1) 指示燈 DS0

2) 四個按鍵

3) 串口

4) TFTLCD 模塊

5) XM8A51216

這些我們都已經介紹過,接下來我們開始軟件設計。

42.3 軟件設計

打開本章實驗工程可以看到,我們新增了 MALLOC 分組,同時在分組中新建了文件

malloc.c 以及頭文件 malloc.h。 內存管理相關的函數和定義主要是在這兩個文件中。

打開 malloc.c 文件,代碼如下:

//內存池(32 字節對齊)

__align(32) u8 mem1base[MEM1_MAX_SIZE]; //內部 SRAM 內存池

__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));

//外部 SRAM 內存池

__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000)));

//內部 CCM 內存池

//內存管理表

u16 mem1mapbase[MEM1_ALLOC_TABLE_SIZE];

//內部 SRAM 內存池 MAP

u16 mem2mapbase[MEM2_ALLOC_TABLE_SIZE] __attribute__((at(0X68000000

+MEM2_MAX_SIZE)));

//外部 SRAM 內存池 MAP

u16 mem3mapbase[MEM3_ALLOC_TABLE_SIZE] __attribute__((at(0X10000000

+MEM3_MAX_SIZE)));

//內部 CCM 內存池 MAP

//內存管理參數

const u32 memtblsize[SRAMBANK]={MEM1_ALLOC_TABLE_SIZE,

MEM2_ALLOC_TABLE_SIZE,MEM3_ALLOC_TABLE_SIZE}; //內存表大小

const u32 memblksize[SRAMBANK]={MEM1_BLOCK_SIZE,MEM2_BLOCK_SIZE,

MEM3_BLOCK_SIZE};

//內存分塊大小

const u32 memsize[SRAMBANK]={MEM1_MAX_SIZE,MEM2_MAX_SIZE,

MEM3_MAX_SIZE};

//內存總大小

//內存管理控制器

struct _m_mallco_dev mallco_dev=

{

my_mem_init,

//內存初始化

my_mem_perused,

//內存使用率

mem1base,mem2base,mem3base,

//內存池

mem1mapbase,mem2mapbase,mem3mapbase,//內存管理狀態表

0,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

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

//標注內存塊非空

{

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

{

int index=offset/memblksize[memx];

//偏移所在內存塊號碼

int nmemb=mallco_dev.memmap[memx][index]; //內存塊數量

for(i=0;i

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); //返回新內存首地址

}

}

這里,我們通過內存管理控制器 mallco_dev 結構體(mallco_dev 結構體見 malloc.h),實現 對三個內存池的管理控制。為甚

首先,是內部 SRAM 內存池,定義為:

__align(32) u8 mem1base[MEM1_MAX_SIZE];

然后,是外部 SRAM 內存池,定義為:

__align(32) u8 mem2base[MEM2_MAX_SIZE] __attribute__((at(0X68000000)));

最后,是內部 CCM 內存池,定義為:

__align(32) u8 mem3base[MEM3_MAX_SIZE] __attribute__((at(0X10000000)));

這里之所以要定義成 3 個,是因為這三個內存區域的地址都不一樣,STM32F4 內部內存分 為兩大塊:1,普通內存(又分為主要內存和輔助內存,地址從:0X2000 0000 開始,共 128KB), 這部分內存任何外設都可以訪問。2,CCM 內存(地址從:0X1000 0000 開始,共 64KB),這 部分內存僅 CPU 可以訪問,DMA 之類的不可以直接訪問,使用時得特別注意!! 而外部 SRAM,地址是從 0X6800 0000 開始的,共 1024KB。

所以,這樣總共有 3 部分內 存,而內存池必須是連續的內存空間,才可以,這樣 3 個內存區域,就有 3 個內存池,因此, 分成了 3 塊來管理。

其中,MEM1_MAX_SIZE、MEM2_MAX_SIZE 和 MEM3_MAX_SIZE 為在 malloc.h 里面 定義的內存池大小,外部 SRAM 內存池指定地址為 0X6800 0000,也就是從外部 SRAM 的首地 址開始的,CCM 內存池從 0X1000 0000 開始,同樣是從 CCM 內存的首地址開始的。但是,內 部 SRAM 內存池的首地址則由編譯器自動分配。__align(32)定義內存池為 32 字節對齊,以適應 各種不同場合的需求。

此部分代碼的核心函數為:my_mem_malloc 和 my_mem_free,分別用于內存申請和內存釋 放。思路就是我們在 42.1 接所介紹的那樣分配和釋放內存,不過這兩個函數只是內部調用,外 部調用我們使用的是mymalloc和myfree兩個函數。其他函數我們就不多介紹了,保存malloc.c, 然后,打開 malloc.h,代碼如下:

#ifndef __MALLOC_H

#define __MALLOC_H

#include "stm32f4xx.h"#ifndef NULL

#define NULL 0

#endif

//定義三個內存池

#define SRAMIN

0

//內部內存池

#define SRAMEX 1

//外部內存池

#define SRAMCCM 2

//CCM 內存池(此部分 SRAM 僅僅 CPU 可以訪問!!!)

#define SRAMBANK 3

//定義支持的 SRAM 塊數.

//mem1 內存參數設定.mem1 完全處于內部 SRAM 里面.

#define MEM1_BLOCK_SIZE

32

//內存塊大小為 32 字節

#define MEM1_MAX_SIZE

100*1024

//最大管理內存 100K

#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

//內存表大小

//mem3 內存參數設定.mem3 處于 CCM,用于管理 CCM(特別注意,這部分 SRAM,僅 CPU 可

//以訪問!!)

#define MEM3_BLOCK_SIZE

32

//內存塊大小為 32 字節

#define MEM3_MAX_SIZE

60 *1024

//最大管理內存 60K

#define MEM3_ALLOC_TABLE_SIZE

MEM3_MAX_SIZE/MEM3_BLOCK_SIZE

//內存表大小

//內存管理控制器

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 里面定義

void mymemset(void *s,u8 c,u32 count);

//設置內存

void mymemcpy(void *des,void *src,u32 n);//復制內存

void my_mem_init(u8 memx);

//內存管理初始化函數(外/內部調用)

u32 my_mem_malloc(u8 memx,u32 size); //內存分配(內部調用)

u8 my_mem_free(u8 memx,u32 offset);

//內存釋放(內部調用)

u8 my_mem_perused(u8 memx);

//獲得內存使用率(外/內部調用)

//用戶調用函數

void myfree(u8 memx,void *ptr);

//內存釋放(外部調用)

void *mymalloc(u8 memx,u32 size);

//內存分配(外部調用)

void *myrealloc(u8 memx,void *ptr,u32 size);//重新分配內存(外部調用)

#endif

這部分代碼,定義了很多關鍵數據,比如內存塊大小的定義:MEM1_BLOCK_SIZE、

MEM2_BLOCK_SIZE 和 MEM3_BLOCK_SIZE,都是 32 字節。內存池總大小,內部 SRAM 內

存池大小為 100K,外部 SRAM 內存池大小為 960K,內部 CCM 內存池大小為 60K。

MEM1_ALLOC_TABLE_SIZE、MEM2_ALLOC_TABLE_SIZE 和 MEM3_ALLOC_TABLE_

SIZE,則分別代表內存池 1、2 和 3 的內存管理表大小

從這里可以看出,如果內存分塊越小,那么內存管理表就越大,當分塊為 2 字節 1 個塊的

時候,內存管理表就和內存池一樣大了(管理表的每項都是 u16 類型)。顯然是不合適的,我們

這里取 32 字節,比例為 1:16,內存管理表相對就比較小了。

其他就不多說了,大家自行看代碼理解就好。接下來我們看看主函數代碼:

int main(void)

{

u8 key; u8 i=0; u8 *p=0;u8 *tp=0;

u8 paddr[18];

//存放 P Addr:+p 地址的 ASCII 值

u8 sramx=0;

//默認為內部 sram

HAL_Init();

//初始化 HAL 庫

Stm32_Clock_Init(336,8,2,7);

//設置時鐘,168Mhz

delay_init(168);

//初始化延時函數

uart_init(115200);

//初始化 USART

usmart_dev.init(84);

//初始化 USMART

LED_Init();

//初始化 LED

KEY_Init();

//初始化 KEY

LCD_Init(); //初始化 LCD

SRAM_Init();

//初始化外部 SRAM

my_mem_init(SRAMIN);

//初始化內部內存池

my_mem_init(SRAMEX);

//初始化外部內存池

my_mem_init(SRAMCCM);

//初始化 CCM 內存池

POINT_COLOR=RED;//設置字體為紅色

LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");

LCD_ShowString(30,70,200,16,16,"MALLOC TEST");

LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

LCD_ShowString(30,110,200,16,16,"2014/5/15");

LCD_ShowString(30,130,200,16,16,"KEY0:Malloc KEY2:Free");

LCD_ShowString(30,150,200,16,16,"KEY_UP:SRAMx KEY1:Read");

POINT_COLOR=BLUE;//設置字體為藍色

LCD_ShowString(30,170,200,16,16,"SRAMIN");

LCD_ShowString(30,190,200,16,16,"SRAMIN USED: %");

LCD_ShowString(30,210,200,16,16,"SRAMEX USED: %");

LCD_ShowString(30,230,200,16,16,"SRAMCCM USED: %");

while(1)

{

key=KEY_Scan(0);//不支持連按

switch(key)

{

case 0://沒有按鍵按下

break;

case KEY0_PRES: //KEY0 按下

p=mymalloc(sramx,2048);//申請 2K 字節

if(p!=NULL)sprintf((char*)p,"Memory Malloc Test%03d",i);//向p寫入內容

break;

case KEY1_PRES: //KEY1 按下

if(p!=NULL)

{

sprintf((char*)p,"Memory Malloc Test%03d",i);//更新顯示內容

LCD_ShowString(30,270,200,16,16,p);//顯示 P 的內容

}

break;

case KEY2_PRES: //KEY2 按下

myfree(sramx,p); //釋放內存

p=0;

//指向空地址

break;

case WKUP_PRES:

//KEY UP 按下

sramx++;

if(sramx>2)sramx=0;

if(sramx==0)LCD_ShowString(30,170,200,16,16,"SRAMIN ");

else if(sramx==1)LCD_ShowString(30,170,200,16,16,"SRAMEX ");

else LCD_ShowString(30,170,200,16,16,"SRAMCCM");

break;

}

if(tp!=p)

{

tp=p;

sprintf((char*)paddr,"P Addr:0X%08X",(u32)tp);

LCD_ShowString(30,250,200,16,16,paddr); //顯示 p 的地址

if(p)LCD_ShowString(30,270,200,16,16,p);//顯示 P 的內容

else LCD_Fill(30,270,239,266,WHITE);

//p=0,清除顯示

}

delay_ms(10);

i++;

if((i%20)==0)//DS0 閃爍.

{

LCD_ShowNum(30+104,190,my_mem_perused(SRAMIN),3,16);//顯示使用率

LCD_ShowNum(30+104,210,my_mem_perused(SRAMEX),3,16);//顯示使用率

LCD_ShowNum(30+104,230,my_mem_perused(SRAMCCM),3,16);//使用率

LED0=!LED0;

該部分代碼比較簡單,主要是對 mymalloc 和 myfree 的應用。不過這里提醒大家,如果對 一個指針進行多次內存申請,而之前的申請又沒釋放,那么將造成“內存泄露”,這是內存管理 所不希望發生的,久而久之,可能導致無內存可用的情況!所以,在使用的時候,請大家一定 記得,申請的內存在用完以后,一定要釋放。

42.4 下載驗證

在代碼編譯成功之后,我們通過下載代碼到 ALIENTEK 探索者 STM32F4 開發板上,得到

如圖 42.4.1 所示界面:圖 42.4.1 程序運行效果圖

可以看到,所有內存的使用率均為 0%,說明還沒有任何內存被使用,此時我們按下 KEY0,

就可以看到內部 SRAM 內存被使用 2%了,同時看到下面提示了指針 p 所指向的地址(其實就 是被分配到的內存地址)和內容。多按幾次 KEY0,可以看到內存使用率持續上升(注意對比 p 的值,可以發現是遞減的,說明是從頂部開始分配內存!),此時如果按下 KEY2,可以發現 內存使用率降低了 2%,但是再按 KEY2 將不再降低,說明“內存泄露”了。這就是前面提到 的對一個指針多次申請內存,而之前申請的內存又沒釋放,導致的“內存泄露”。

按 KEY_UP 按鍵,可以切換當前操作內存(內部 SRAM 內存/外部 SRAM 內存/內部 CCM 內存),KEY1 鍵用于更新 p 的內容,更新后的內容將重新顯示在 LCD 模塊上面。

總結

以上是生活随笔為你收集整理的正点原子的内存管理_正点原子【STM32-F407探索者】第四十二章 内存管理实验的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。