?malloc()用的是什么算法;free()是怎样释放内存的

amoogo 2006-10-31 11:29:55
在 K&R 第二版中关于malloc()是这样描述的:
malloc并不是从一个在编译时就确定的固定的固定大小的数组中分配存储空间,而是在需要时向系统申请。因为程序中的某些地方不通过malloc调用申请空间,所以malloc管理的空间不一定是连续的。这样空闲存储空间以空闲链表组织,每一块包含一个长度、一个指向下一块的指针和一个指向自身空间的指针。当有申请时,malloc会扫描空闲链表,直到找到一个足够大的为止(首次适应)。与之相对应的是“最佳适应”,它寻找满足条件的最小块。如果块的大小正好,就从链表移走并返给客户,如果太大就分成两部分:大小合适的返给客户,剩下的留给空闲链表。如果没有足够大的,就向系统申请。
释放过程首先搜索空闲链表,以找到可以插入被释放块的合适位置。如果与被释放块相邻的任一边是一个空闲块,则合成一个更大的。
上面提到malloc()有两种算法,即“首次适应”和“最佳适应”,实际用的是什么
free()一次释放的并不是malloc()申请的所有内存,怎么回事
还是说这个不是标准库函数的用法。
...全文
669 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
amoogo 2006-10-31
  • 打赏
  • 举报
回复
感谢:
lj860603(键键->最近神经错乱)
wanfustudio(雁南飞:知识之败,慕虚名不务潜修也)
jixingzhong(瞌睡虫:选择了远方,只有风雨兼程!)
jixingzhong 2006-10-31
  • 打赏
  • 举报
回复
UNIX中Malloc和Mfree函数的实现原理
文展  成都信息工程学院 电子系 (成都 610041)
唐翔弘  重庆邮电学院 计算机科学与技术学院 (重庆 400065)

摘 要 本文主要介绍了早期的UNIX操作系统内核中两个内存操作函数的实现原理和技巧。在大多数操作系统内核中,提供给用户的内存操作函数基本上都是Malloc和Mfree两个函数,或者是与之相近的函数。这两个函数也充分体现了举世闻名的UNIX操作系统精巧的内存管理方法和技巧。

关键词 资源图 可用列表 主存 盘交换区


1. 引言
在我们平常的编程过程中,我们的程序经常会涉及到内存的操作,也因此常常调用到内存的分配和释放函数。无论是什么语言工具, 都不可避免的会提供给程序员这两个配套的函数,其中C语言中的Malloc函数和Mfree函数是此类型函数的经典代表。那到底在操作系统级的低层,系统是如何实现对内存的合理管理和具体分配,这些都是应用程序员平常在计算机领域都很少去了解的知识。其实这两个函数的实现原理和技巧,可以给程序开发人员在编程技巧上以极大的启发,同时还可以帮助提高程序设计人员在程序设计思维上的技巧。了解这两个函数对我们爱好计算机的程序员来说,都是有着积极的意义的。



2. 资源图记录可用区列表的介绍
在早期的UNIX操作系统内核中,有一个名为Malloc.c的文件,此文件由两个过程组成:malloc和mfree。这两个过程涉及到了两类存储资源的分配和释放,这两类存储资源是:
? 主存: 它以32个字(64字节)为单位。
? 盘交换区:它以256字(512字节)为单位。
对于这两类资源的每一类,各有一些资源图(coremap或swapmap)记录可用区列表。指向相应资源图的一个指针作为参数传递给“malloc”和“mfree”,所以这两个过程无需了解它们正处理的资源的类型。
“coremap”和“swapmap”都是类型为“map”的结构体数组,其中“map”的结构
体由两个字符指针,亦即无符号整数组成。
2.1 资源图列表维护规则
针对与上述的资源图可用区列表,必然有一套事先规定并一致统一的规则来维护操作这个列表。只有这样,才能达到资源管理的一致性,高效性和合理性。在UNIX操作系统内核里,定义了如下三个规则来维护管理资源图可用区列表。
? 每个可用存储区由其长度和相对地址定义(按相应资源的单位计算)。
? 每个列表中的各元素总是按照相对地址从低到高排列。任何两个相邻列表元素所描述的可用存储区如果构成一个连续可用区,则总是立即将它们合并成一个区。
? 从第一个元素开始顺序逐个查看就能扫描到整个列表,当查看到一个零长元素时结束扫描。此最后一个元素并不是有效列表部分,而是该列表有效部分的技术标志。
上述规则提供了对“malloc”和“mfree”函数的完整规格说明,但是有一个方面是例
外的。这个例外的方面就是:当有多种方式可进行资源分配时,我们必须说明在具体实现时选择哪一种。在一般的数据结构书籍中都会提到内存分配的两种算法:首次适配算法和最佳适配算法。在这里,“malloc”所采用的方法是“首次适配”算法(“First Fit” )。
2.2 资源图列表维护举例
下面举一个例子说民资源图是如何维护的,假定下列三个资源区是可用的:
? 一个可用区的长度为17,在位置20处开始,在36处结束。
? 一个可用区的长度为14,在位置43处开始,在56处结束。
? 一个可用区的长度为8, 在位置63处开始,在70处结束。
于是,资源图应当包含:

如果接到一个长度为8的存储空间请求,则从位置20处开始分配,分配后的资源图变成如下图所示:

如果从地址37开始到42结束的存储区返回至可用区列表,则资源图变成如下图所示:




3. Malloc函数的实现
3.1 Malloc函数实现的模拟源代码
下面是Malloc函数操作资源图可用列表的模拟源代码:
01: struct map
02: {
03: char *m_size;
04: char *m_addr;
05: }
06: malloc(map , size)
07: struct map *p;
08: {
09: resister int a ;
10: resister struct map *bp;
11: for(bp = mp; bp->m_size>0; bp++)
12: {
13: if(bp->m_size >= size)
14: {
15: a = bp->m_addr;
16: bp->m_addr = + size;
17: if ( (bp->m_size = - size) = =0)
18: {
19: do {
20: bp++;
21: (bp-1)->m_addr = bp->m_addr;
22: }while( (bp-1)->m_size = bp->m_size);
23: }
24: return(a);
25: }
26: }
27: return(0);
28: }

3.2 Malloc函数模拟源代码解析
此过程体由一“for”循环组成,它搜索“map”数组,直至:
1)到达可用资源列表的尾端;或
2)搜索到一个区,其长度能满足当前请求
11: “for”语句首先对“bp”赋初值,使其指向资源图的第一个元素。在每一次循环时,“bp”增1指向下一个“map”结构。
在这里,循环判断表达式是针对资源列表的结束符“0”元素。
13: 若该列表元素定义了一个区域,其长度至少与所要求的相等,则……
15:记住该区的第一个单元的地址。
16: 增加存放在该数组元素中的地址。
17:减少存放在该数组元素中的长度,并将结果与0比较(也就是检查是否恰好适配)。
19:如果精确适配,则将所有后续元素(直至并包括结束标志元素)都下移一位置。注意:“(bp - 1)”指向“bp”所引用的前一个结构。
22:“while”的继续条件并不测试“(bp-1)->m_size”和“bp->m_size”是否相等。被测试的值是由“bp->m_size”赋予“(bp-1)->m_size”的值。
24:返回该区域的地址。Return语句结合了“malloc”过程,因此也就结束了“for”循环。
最后的返回0意味着“不幸”。这基于下列事实:没有一个有效区会在单元0处开始。



4. Mfree函数的实现
4.1 Mfree函数实现的模拟源代码
下面是Mfree函数操作资源图可用列表的模拟源代码:
01: malloc(map , size, aa)
02: struct map *mp;
03: {
04: resister int a ;
05: resister int t ;
06: resister struct map *bp;
07: a = aa;
08 for(bp = mp; bp->m_addr <= a && bp->m_size != 0; bp++)
09: ;
10: if (bp>mp && (bp-1)->m_addr + (bp-1)->m_size= =a)
11: {
12: (bp-1)->m_size = + size;
13: if (a+size = = bp->m_addr)
14: {
15: (bp-1)->m_size = + bp->m_size;
16: while(bp->m_size > 0)
17: {
18: bp++;
19: (bp-1)->m_addr = bp->m_addr;
20: (bp-1)->m_size = bp->m_size;
21: }
22: }
23: }
24: else
25: {
26: if(a+size = =bp->m_addr && bp->m_size > 0)
27: {
28: bp->m_addr = - size;
29: bp->m_size = + size;
30: }
31: else if(size > 0)
32: {
33: do{
34: t = bp->m_addr;
35: bp->m_addr = a;
36: a = t;
37: t = bp->m_size;
38: bp->m_size = size ;
39: bp ++;
40: }while(size = t)
41: }
42: }
43: }

4.2 Mfree函数模拟源代码解析
此过程将一个存储区返回给由“map”指定的“资源图”,该取的长度为“size”,其起始地址为“aa”。此过程的体由一行“for”语句和占多行的一条“if”语句组成。
08:步进“bp”直至遇到一元素:
该元素的地址大于所返回区的地址,亦即不满足条件:
i.e.not “bp->m_addr <= a”;
该元素的列表结束标志元素,亦即它不满足条件:
i.e.not “bp->m_size !=0” ;
09: 此行的分号是很重要的,它终止了一条空语句。
10:我们已找到在其之前应插入新列表元素的元素。问题是:该列表是增大一个元素,还是由于合并,该列表仍保持原先的元素数,甚至其元素数减少1?如果“bp>mp”,那么我们就不会在列表开始处插入。如果:(bp-1)->m_addr + (bp-1)->m_size == a,那么正被返回的存储区与列表中的前一元素描述的存储区紧相邻接。
12:将前1元素的长度增加正被返回区的长度。
13:正被返回区是否与列表中下一个元素间相链接。如果是这样……
15:将下一列表元素的长度加至前一元素的长度。
16:将所有余下的列表元素向下移动1个位置,直至并包括该列表元素结束标志元素。在这里,若13行的测试在“bp->m_size”为0时意外的产生结果为真的情况,也不会造成任何损害。
26:如果第10行的测试失败,则执行此语句,亦即正被返回的区不能与列表中的前一元素区相合并。至于能否与下一元素区合并呢?注意本语句中对下一元素是否为空(null)的检查。
31:假定正被返回的区非空(在进入本过程时就应对此做检查),则在列表元素中加一个新元素,然后将所有余下的元素向上(也就是向数组下标增大方向)移动一个位置。


5. 结论
由上面的模拟代码我们可以看出,通过对用资源图可用区列表的有效操作,这种管理方式使操作系统做到了对内存分配和释放的良好效果,既最大程度的利用了内存空间,又较好的提高了执行效率。实在是操作系统内核管理的精巧设计。对我们今后在各种系统设计的过程中,都能提供好的帮助,可以大大启发我们的设计思维。


参考文献
1. Manrice J.Bach. Unix操作系统设计[J]. 北京:机械工业出版社,2000.4
2. 侯业勤,张箐 . 分布式嵌入式实时操作系统QNX[M]. 宇航出版社,1999.1
3. Michael Barabanov,Victor Yodaiken. Real-time Linux[J]. Linux
journal,1997.2

4. Michael Beck, Harald Bohme et al. LINUX Kernel Internals .
Addison-Wesley,1996
飞哥 2006-10-31
  • 打赏
  • 举报
回复
free只释放了指向内存的指针·
lj860603 2006-10-31
  • 打赏
  • 举报
回复
free()应该只是记录了一些信息,然后告诉操作系统那块内存可以去释放,
具体怎么告诉操作系统的我不清楚,释放的应该是操作系统的事吧。

PS:说错了的话,勿怪。
飞哥 2006-10-31
  • 打赏
  • 举报
回复
首次适应算法
lj860603 2006-10-31
  • 打赏
  • 举报
回复
操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,
然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,
并将该结点的空间分配给程序。
malloc()申请的空间实际应该是分了两个不同性质的空间。一个就是用来记录管理信息的空间,另外一个就是可用空间了。而用来记录管理信息的实际上是一个结构体。
playboy1983 2006-10-31
  • 打赏
  • 举报
回复
关注中
colorslife 2006-10-31
  • 打赏
  • 举报
回复
关注,期待高手
Thinking_Moo 2006-10-31
  • 打赏
  • 举报
回复
同样疑问,关注中

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧