UNIX中Malloc和Mfree函数的实现原理

本文深入剖析了操作系统内存管理机制,特别是通过资源图记录可用区列表的方式,详细解释了经典的内存分配函数Malloc与释放函数Mfree的工作原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

     

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

 

2.资源图记录可用区列表的介绍

在早期的UNIX操作系统内核中,有一个名为Malloc.c的文件,此文件由两个过程组成:mallocmfree。这两个过程涉及到了两类存储资源的分配和释放,这两类存储资源是:

?主存: 它以32个字(64字节)为单位。

?盘交换区:它以256字(512字节)为单位。

对于这两类资源的每一类,各有一些资源图(coremapswapmap)记录可用区列表。指向相应资源图的一个指针作为参数传递给“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结束的存储区返回至可用区列表,则资源图变成如下图所示:

 

 

 

 

3Malloc函数的实现

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处开始。

 

 

 

4Mfree函数的实现

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.结论

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值