第四三天 2015年09月02日 周二 例程:内存管理实验(三)
一.先把昨天没有想明白的问题解决掉:做关于内存管理实验较为容易碰到的报错如下
大致就是定义变量(地址)地址重叠/冲突,主要是与地址有关,如果做实验的小伙伴从上一个SRAM实验通过添加程序进行内存管理实验忘了删除原来定义的u32 testsram【250000】就会进行上述类似报错,那么如果多次指定不同数组起始地址,什么情况会报错及如何解决呢?
为了测试这个问题,我们可以定义三个数组,均是32位数据类型25个数组元素,即每个数组占25*4=100字节
第一种情况:(如下)三个数组同一指定起始地址,是否正确?正确!
三个数组就像同一级别的数组定义顺序排布
[mw_shl_code=c,true]u32 testsram1[25] __attribute__((at(0x68000000))); //①所测起始地址0X68000000
u32 testsram2[25] __attribute__((at(0x68000000))); //②所测起始地址0X68000064
u32 testsram3[25] __attribute__((at(0x68000000))); //③所测起始地址0X680000C8[/mw_shl_code]
第二种情况:(如下)三个数组两个同一起始地址,一个起始地址后移100自己,即一个数组占用空间,是否正确?错误!
这也就是我们从SRAM实验到内存管理实验没有删除testsram数组造成报错的情况,为什么此时会报错?如何解决?我们继续看其他情况,然后从中找出问题所在...
[mw_shl_code=c,true]u32 testsram1[25] __attribute__((at(0x68000000)));
u32 testsram2[25] __attribute__((at(0x68000000+25*4)));
u32 testsram3[25] __attribute__((at(0x68000000)));[/mw_shl_code]
第三种情况:(如下)在第二种情况基础上,进一步右移,使其右移200字节,也就是定义的两个数组所占空间,是否正确?正确!
与第二种情况对比,我们可以发现,貌似每个__attribute__就像是在它指定的地址划了一条“三八”线,这条线不可随意跨越,在线之前用指定地址存放的数据必须在线之前存储完毕,同一指定起始地址就视为同等级别,依次放置,如果在两条"三八"线之间数据没有存放完整只有一个结果-----报错。
[mw_shl_code=c,true]u32 testsram1[25] __attribute__((at(0x68000000))); //①所测起始地址0X68000000
u32 testsram2[25] __attribute__((at(0x68000000+25*4*2))); //②所测起始地址0X680000C8
u32 testsram3[25] __attribute__((at(0x68000000))); //③所测起始地址0X68000064[/mw_shl_code]
另:以上三个数组是并列关系,谁在前谁在后只是各自对应地址会有变化,不存在先后顺序关系,即如上图,假如①和③调换位置,各自对应地址也会交换,而②无论在什么位置地址都是固定的,因为它的指定地址与另外两个不同,是固定的。
第四种情况:(如下)同一数组两个指定起始地址,是否正确?正确!
这种情况此数组的起始地址以第二次指定地址为准,即如果下图中①与③位置对换,则testsram1起始地址为0X68000000,而如下图情况时testsram1的起始地址为0X68000064。
[mw_shl_code=c,true]u32 testsram1[25] __attribute__((at(0x68000000))); //①
u32 testsram2[25] __attribute__((at(0x68000000))); //②所测起始地址0X68000000
u32 testsram1[25] __attribute__((at(0x68000000+25*4))); //③所测起始地址0X68000064[/mw_shl_code]
总结:1.对同一块区域使用attribute,我们可以直接指定多个数据同一起始地址,程序会自动依次存放,既简单又省事。
2.如果要指定不同起始地址,确保每次前一次attribute所指定地址到第二次attribute指定地址之间的变量有足够空间存放,才能第二次使用attribute指定地址。
3.对于attribute不熟悉,网上资料没看明白,深究意义也不是很大,故在此仅作以上测试层面简单分析。
二、内存管理实验中,是如何在三个内存空间实现切换的?
1.首先定义一个结构体来统一管理程序常用函数/变量
[mw_shl_code=c,true]//内存管理控制器
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里面定义[/mw_shl_code]
2.定义完以后初始化是必须的
[mw_shl_code=c,true]//内存管理控制器
struct _m_mallco_dev mallco_dev=
{
my_mem_init, //内存初始化
my_mem_perused, //内存使用率
mem1base,mem2base,mem3base, //内存池
mem1mapbase,mem2mapbase,mem3mapbase,//内存管理状态表
0,0,0, //内存管理未就绪
};[/mw_shl_code]
3.初始化中前两行是两个函数元素,前面已经简单分析过,我们主要看第三四行的数组赋值。第三四行赋值来源如下
[mw_shl_code=c,true]//内存池(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[/mw_shl_code]
也就是将内存池/内存管理表的数组指针(地址)赋值给返回值为指针的(内存池/内存管理表)数组。
4.通过数组下标来进行不同区域内存管理。
[mw_shl_code=c,true]//内存管理初始化
//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
}
[/mw_shl_code]
其中,mymemset函数如下
[mw_shl_code=c,true]//设置内存
//*s:内存首地址
//c :要设置的值
//count:需要设置的内存大小(字节为单位)
void mymemset(void *s,u8 c,u32 count)
{
u8 *xs = s;
while(count--)*xs++=c;
} [/mw_shl_code]
三、开发指南中,内存管理实验实际是定义了一个大的数组,内存分配与释放是对这个大的数组进行的部分元素的使用与释放,这个数组是放在静态存储区的而非堆区,只有使用了系统malloc函数申请的空间才会放在堆区中(具体请看相关/参考链接【1】中各楼层讨论介绍)。
内存组成分为一下几部分:【注,如与其他资料描述相悖,请不要以此为准】
1.静态存储区(全局区static),全局变量和静态变量存储区域,初始化的全局变量和静态变量存放在一块区域,而为初始化的全局变量和静态变量存放在相邻另一区域,未初始化的程序结束后由系统释放。
2.堆区(heap),一般由程序员分配释放,分配方式类似于链表。
3.栈区(stack),编译器自动分配释放,存放函数参数值、局部变量值等。
4.文字常量区,常量字符串存放区域,const之类常量,是存放在flash中的。
5.程序代码区,存放函数体的二进制代码。
网络大家比较推荐的一个前辈程序
[mw_shl_code=c,true] //main.cpp
int a = 0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; //栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); // 分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); // 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
} [/mw_shl_code]
【结合STM32F1中文参考手册(较为熟悉,容易找到所需框图)与STM32F4中文参考手册进行总结】
上图截自stm32f103参考手册,可与CM3权威指南框图进行对照学习,如下
此时,可对存储区域稍作整理
参考/相关链接:
【1】http://www.openedv.com/posts/list/0/24152.htm(此帖及各楼层讨论非常值得反复学习思考)
【2】http://blog.chinaunix.net/uid-11959329-id-2797040.html
【3】http://www.openedv.com/posts/list/28280.htm
【4】http://www.cnblogs.com/hanyonglu/archive/2011/04/12/2014212.html
【5】http://wenku.baidu.com/link?url=UKhX5hsDqEJ-CFWexV64Qo6WK_IrcAwGe6TrMpdE6S1WOMLptQY5lovTi1e0peGUdB5Og5hMlmAT0MBzkRwwsqYHXGTzboYmfGrk_ZUaEdS
【6】http://blog.csdn.net/slj_win/article/details/16906141
【7】http://www.openedv.com/posts/list/39809.htm
【8】http://www.openedv.com/posts/list/23456.htm
【9】http://www.openedv.com/posts/list/58590.htm
【10】http://www.openedv.com/posts/list/45339.htm
【11】http://blog.163.com/xuanmingzhiyou@yeah/blog/static/14247767620122111272980/
【12】http://blog.csdn.net/l_chxu/article/details/8841668
【13】http://www.cnblogs.com/hanyonglu/archive/2011/04/28/2031271.html
【14】http://www.cnblogs.com/longyi1234/archive/2010/03/22/malloc.html
【15】http://www.jb51.net/article/36391.htm
【16】http://www.openedv.com/posts/list/26805.htm
【17】http://www.openedv.com/posts/list/26602.htm
|