动态内存分配的实现
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
......
*Next *Last Size
*Next *Last Size
*Next *Last Size
......
......
......
用户内存块的结构
HeadChk Size EndChk
_UsingMem结构体 (用户内存块头)
可使用的用户内存块
此图为单个用户内存块的结构.即申请到的内存块的结构.
p=(LinkList)malloc(sizeof(LNode));
p->data=a;
p->link=NULL;
if(list==NULL)
list=p;
else
r->link=p;
r=p;
}
return(list);
}
当我看到“p=(LinkList)malloc(sizeof(LNode));”这句程序时,我就没心思继续往下看了。我在想 malloc() 这个函数究竟是如何实现的呢?以前在网上也看到有很多人说起 malloc()这个函数,但是因为自已从来都没 用过,所以一直都没去想像与此相关的问题。今天是第一次看到它的使用,所以就想到了它是如何实现的这 个问题了。
说到内存管理,我又想到 UCOSII。以前看过一些邵贝贝翻译的 UCOSII,里面有一章专门讲述内存管理, 由于当时根本不知道内存管理是什么东西,所以就随便翻过去了。现在似乎明白了一些东西,所以又翻开了 这本书,找到内存管理这一章,截图如下:
呵呵,心里好高兴!malloc()果然是和内存管理有着密切的联系。至此,我并没有找到问题的答案,但我 知道了内存管理这个概念,这才是令我高兴的原因。但我并没有开始分析 UCOSII 中内存管理相关的代码。 因为我又想到了另外一个 OS,一个更小的 OS,那就是周立功公司的 Small RTOS。我想这个里面应该也有 内存管理方面的内容吧,打开它的说明文档,截图如下:
**
**
(c) Copyright 2002-2003, chenmingji
**
All Rights Reserved
**
**
V1.20.0
**
**
**--------------文件信息--------------------------------------------------------------------------------
#if EN_OS_MEM_CHK > 1 #ifndef EN_UserMemChkErr #define EN_UserMemChkErr 1 #endif #endif
/* 分配给用户的内存块的头 */ struct _UsingMem { #if EN_OS_MEM_CHK > 0
unsigned int HeadChk; #endif
我的第一想法就是,我可以在 KEIL 中调用 malloc()函数,然后分析编译出来的汇编代码,从而找到答案。 但我马上又想到,这个问题是不是很久以前已经有人分析过了,也许可以在网上找到现成的答案(呵呵,我 是不是好懒!)。我搜索了诸如“malloc()的实现”,“mallo() c51”,“动态内存分配”这样的关键字。这样我 就被引到了“内存管理”这个问题上了。
OSMemFree(ADDR1);
OSMemFree(ADDR2);
从高地址开始分配.
OSMemNew(30);的返回值是
用户内存块的首地址,即AD 0xa55a
DR1=2070.
36
0x5aa5
30个字节
......
20个字节
0xa55a 26 0x5aa5
......
0xa55a
36
此处地址值为2070(ADDR1) 0x5aa5
Size *Next *Last
......
_FreeMem结构体 (自由内存块头)
在执行OSMemInit(void xdata *Addr,unsigned int MemSize)函数时, 会把参数MemSize的值赋给_FreeMem结构 体中的Size成员变量, Next和Last成员变量为空.
NULL
100
NULL NULL 64
NULL NULL 38
2064 N UL L 38
NULL NULL 100
......
......
......
......
......
OSMemInit(2000, 100);
100个字节
ADDR1=OSMemNew(30);
ADDR2=OSMemNew(20);
** 创建人: 陈明计
** 版 本: V1.20.0
** 日 期: 2003 年 8 月 3 日
** 描 述: 原始版本
**
**------------------------------------------------------------------------------------------------------
附录_源程序:
/*********************************************************************************************************
**
Small RTOS(51)
**
The Real-Time Kernel(For Keil c51)
......
此处地址值为2044(ADDR2)
此时释放内存,只须修改链结 点的内容,使两个自由块互相 链接起来即可.
......
......
0xa55a
36
0x5aa5
此处地址值为2064
N UL L
2000 此时释放内存,分两个步骤: 36 1.因为被释放的内存块与上一
个自由块相邻,所以把这块内 存与上一个相邻的自由块合 并. 2.完成第一步后,发现两个自 由块相邻,所以再把这两上自 由块合并在一起.
动态内存分配的实现
梅林 mlnin@163.com
2007 年 6 月 15 日,我在看一本数据结构的书。书上讲到如何创建一个链表,方法是,先定义一个结构 体来描述一个链结点类型,如下:
Typedef struct node
{
ElemType data;
Struct node *link;
ቤተ መጻሕፍቲ ባይዱ
}LNode,*LinkList;
当在程序中多次申请和释放内存时,在动态内存分配区就会形成多个这样的 块结构,这些块之间没有任何联系. HeadChk和EndChk为校验字,在申请内存的时候会分别赋值为0xa55a和0x5a a5.在释放这块内存的时候会检查这两个值,如果前后不一致,就会调用User MemChkErr()函数,用户可以在此函数中添加出错处理代码. 此处的Size值并不是OSMemNew(unsigned int Size)函数
unsigned int Size; #if EN_OS_MEM_CHK > 0
unsigned int EndChk;
/* 校验字,内存回收时检查内存块是否有效 */ /* 内存块所占空间大小 */
/* 校验字,内存回收时检查内存块是否有效 */
#endif };
/* 自由内存块的头,所有自由内存块组成双向链表 */
......
...... ...... ...... ...... ......
至此,动态内存分配的基本过程已经说完了。其实在这个过程中还有许多细节的问题,我并没有一一描 述,比如以下这段代码的作用是什么?这个很重要!
对于这段代码,只要考虑(ThisFreeMem->Size) ==(Size + sizeof(struct _FreeMem))时的情形就会明白了, 我就不多说了,画图好累啊!
struct _FreeMem
{
struct _FreeMem xdata *Next;
/* 指向下一个自由内存块,无则为 NULL */
********************************************************************************************************/
#ifndef EN_OS_MEM_CHK
#define EN_OS_MEM_CHK 1
#endif
哈哈!我想我要找的答案已经快出来了。答案就在 OS_MEM.C 里面。 我把 OS_MEM.H, OS_MEM.C 这两个文件打印了,2+4 总共 6 张纸。大概看了一下程序的框架。总共定 义了两个结构体,struct _UsingMem 和 struct _FreeMem。一共有三个函数 uint8 OSMemInit(),void xdata *OSMemNew(),void OSMemFree()。呵呵,是不是很简单。可我花了整整一个星期才看明白啊! 下面我就来根据这两个文件来分析动态内存分配的过程。错误之处在所难免。多多联系。 我觉得要弄清楚动态内存分配原理,首先要在头脑中有两个内存块模型,一个是自由内存块,一个是用 户内存块。我在阅读程序时,就是因为事先头脑中没有这两个模型,所以才导致读起来非常吃力。 动态内存分配过程中的关键地方我都用图形表达出来了。 在“动态内存分配全过程”这张图中,我举了一个完整的例子,先在内存中初始化一块动态内存分配区 间,接着申请一块内存 A,再申请一块内存 B,接着释放内存 A,再释放内存 B,最后动态内存分配区回到 了初始状态。 我想当初如果有人给我提供这样的图形,我肯定是要不了一个星期就能弄明白的。好了,快看图吧!
自由内存块的结构
*Next *Last Size
_FreeMem结构体 (自由内存块头)
可使用的自由内存块
左图为单个自由内存块的结构. 当在程序中多次申请和释放内存时,在动态内存分 配区就会形成多个这样的块结构,这些块之间通过 块头的*Next和*Last相互链结在一起(如下图所示). 整个块的大小为Size.(刚开始我一直以为Size是可 使用的内存块的大小,所带来的痛苦就不说了! )
**--------------当前版本修订------------------------------------------------------------------------------
** 修改人:
** 日 期:
** 描 述:
**
**------------------------------------------------------------------------------------------------------
**文 件 名: OS_mem.h
**创 建 人: 陈明计
**最后修改日期: 2003 年 8 月 3 日
**描
述: 内存动态分配模块的头文件,可以在非 Small RTOS(51)下使用。在 Small RTOS(51)
**
下必须配置为支持重入栈。
**--------------历史版本信息----------------------------------------------------------------------------
传递进来的Size值,它还要加上头的长度,这一点与自由内存块不同. 在内存回收时,Size值起到重要作用.Size值和用户内存块首地址是内存回收 的凭据.
......
OSMemInit()函数执行过程
......
*OSFreeMem
*MemFree
执行OSMemInit(void xdata *Addr,unsigned int MemSize)函数的时候, 会把参数Addr的值赋给局部变量MemFree, 再将MemFree的值赋给静态变量OSFreeMem. 所以OSFreeMem和MemFree的值相等,它们都 指向自由内存块的起始地址.
自由内存块
OSMemInit()函数中并未说明紧跟在自 由内存块头后面的就是自由内存块, 在阅读OSMemNew()函数和OSMemFr ee()函数的时候才会发现这一点.
......
动态内存分配全过程
......
......
......
......
......
......
此处地址为2000
NULL
然后讲到,建立一个线性链表的过程是一个动态生成链结点并依次将它们链接到链表中的过程,如下:
LinkList CREAT(int n)
{
LinkList p,r,list=NULL;
ElemType a;
int i;
for(i=1;i<=n;i++)
{ READ(a);
/*以某种方法获取一个数据元素 a*/