正点原子例程_内存管理(转)

  该篇内容的代码例程来自正点原子,讲解分析来自于这位博主郭志凯。因为本身涉及到的知识不多,因此该篇内容99.99%都是从博主那里转过来的。

一、Malloc.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#ifndef __MALLOC_H
#define __MALLOC_H
#include "stm32f10x.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板V3
//内存管理 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2015/1/20
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////


#ifndef NULL
#define NULL 0
#endif

//定义两个内存池
#define SRAMIN 0 //内部内存池
#define SRAMEX 1 //外部内存池

#define SRAMBANK 2 //定义支持的SRAM块数.


//mem1内存参数设定.mem1完全处于内部SRAM里面.
#define MEM1_BLOCK_SIZE 32 //内存块大小为32字节
#define MEM1_MAX_SIZE 35*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 800 *1024 //最大管理内存960K
#define MEM2_ALLOC_TABLE_SIZE MEM2_MAX_SIZE/MEM2_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

二、Malloc.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include "malloc.h"        
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板V3
//内存管理 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2015/1/20
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////


//内存池(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); //返回新内存首地址
}
}

三、源码、思想讲解

1. 思想:将内存池分为块,首先定义每个块的字节数,和内存池的总字节数,用总字节数除以每个块的字节数得到块数

2. 内存池,实际上就是一个数组

3. 内存管理块,实际也是一个数组,总元素个数为内存块数,每个元素对应一个内存块,该元素非零时表示该内存块没有被占用

4. 将第一步中的信息用数组保存起来,方便后面的函数调用

5. 将内存抽象为一个结构体,传入的参数分别是,初始化函数,占用率函数,两个内存池(数组)的基地址,两个内存管理状态表(两个u16数组),两个内存池的就绪布尔值

6. 初始化函数,实际上就是将指定内存池(数组)内面的内容全部用0填充,将内存状态表(u16数组)全部用0填充,然后将该内存池的就绪布尔变量置1

7. 计算内存使用率(注意:得到的是块使用的比率,而不是字节使用的比率)

8. 复制,就是按字节依次赋值内容

9. 从起始位置将连续count个字节的区域用c填充

10. 分配内存(内部调用),成功:返回相对于数组首地址的偏移地址。
-> 判断指定的内存池是否已经初始化
-> 若传入参数为0,表示不需要分配,直接返回
-> 通过所需字节数对每个块的字节数分别取整,取余得到所需的连续块数
-> 从最后一个块往前面寻找所需的连续块,例如所需的块为3,当找到连续2块而,下一块已经被使用时,则将已经找的的连续块数清零,再在前面找连续的3块
-> 返回的偏移地址为所需连续块的起始块相对于内存池的偏移地址
-> 将即将用到的内存块对应的内存管理表中的元素置为所需的连续块数

11. 分配内存,首先判断偏移地址是否正确,然后返回连续块的首地址

12. 扩大分配内存,首先分配一个指定的内存,再将旧内存里面的内容拷贝到新内存(这里感觉战舰的源码有问题,旧的内存里面原来没有size个元素,却拷贝size个元素到新的空间),最后释放旧的内存空间,返回新的内存(块)首地址

13. 清除连续的内存块,成功:返回0
-> 首先通过偏移地址除以每个块的字节数,得到起始块的序号
-> 读取起始块对应的内存控制表元素,得到从起始块开始共要清除多少个连续的块
-> 所谓的清除,实际上只是将对应的内存控制表的元素清零,内存中的值未清零。
-> 为某个对象分配元素时,是分配的连续块,清除时,也是清除这几个连续块,不同对象占用不同的连续块,清除时,不会影响其他对象。

14. 内存释放函数,首先得出偏移地址,然后,调用上一步的函数释放内存

-------------本文结束感谢您的阅读-------------