Malloc And Free

http://cache.baidu.com/c?word=malloc%3B%CA%B5%CF%D6%3B%D4%AD%C0%ED&url=http%3A//community%2Ecsdn%2Enet/Expert/TopicView3%2Easp%3Fid%3D5138477&b=19&a=1&user=baidu

 

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

 

http://cache.baidu.com/c?word=malloc%3B%CA%B5%CF%D6%3B%D4%AD%C0%ED&url=http%3A//community%2Ecsdn%2Enet/Expert/TopicView3%2Easp%3Fid%3D5138477&b=19&a=1&user=baidu
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Malloc Lab是一个实验项目,要求我们实现一个动态内存分配器,使其具有与标准C库中的mallocfree、realloc函数相同的功能。我们需要自行定义块的空间结构,并实现find_fit函数和place函数,这两个函数都涉及到指针的操作,需要特别注意,以避免出现BUG。 在实现动态内存分配器时,我们需要考虑以下几个方面: 1. 内存块的分配和释放:我们需要实现mallocfree函数,前者用于分配内存块,后者用于释放内存块。 2. 内存块的合并:当一个内存块被释放时,我们需要将其与相邻的空闲块合并,以便后续的内存分配。 3. 内存块的查找:在分配内存块时,我们需要查找一个合适的空闲块,以满足用户的需求。 以下是一个简单的动态内存分配器的实现,仅供参考: ```c /* Basic constants and macros */ #define WSIZE 4 /* Word and header/footer size (bytes) */ #define DSIZE 8 /* Double word size (bytes) */ #define CHUNKSIZE (1<<12) /* Extend heap by this amount (bytes) */ /* Pack a size and allocated bit into a word */ #define PACK(size, alloc) ((size) | (alloc)) /* Read and write a word at address p */ #define GET(p) (*(unsigned int *)(p)) #define PUT(p, val) (*(unsigned int *)(p) = (val)) /* Read the size and allocated fields from address p */ #define GET_SIZE(p) (GET(p) & ~0x7) #define GET_ALLOC(p) (GET(p) & 0x1) /* Given block ptr bp, compute address of its header and footer */ #define HDRP(bp) ((char *)(bp) - WSIZE) #define FTRP(bp) ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE) /* Given block ptr bp, compute address of next and previous blocks */ #define NEXT_BLKP(bp) ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE))) #define PREV_BLKP(bp) ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE))) /* Global variables */ static char *heap_listp = 0; /* Function prototypes for internal helper routines */ static void *extend_heap(size_t words); static void *coalesce(void *bp); static void *find_fit(size_t asize); static void place(void *bp, size_t asize); /* * mm_init - Initialize the memory manager */ int mm_init(void) { /* Create the initial empty heap */ if ((heap_listp = mem_sbrk(4*WSIZE)) == (void *)-1) return -1; PUT(heap_listp, 0); /* Alignment padding */ PUT(heap_listp + (1*WSIZE), PACK(DSIZE, 1)); /* Prologue header */ PUT(heap_listp + (2*WSIZE), PACK(DSIZE, 1)); /* Prologue footer */ PUT(heap_listp + (3*WSIZE), PACK(0, 1)); /* Epilogue header */ heap_listp += (2*WSIZE); /* Extend the empty heap with a free block of CHUNKSIZE bytes */ if (extend_heap(CHUNKSIZE/WSIZE) == NULL) return -1; return 0; } /* * mm_malloc - Allocate a block with at least size bytes of payload */ void *mm_malloc(size_t size) { size_t asize; /* Adjusted block size */ size_t extendsize; /* Amount to extend heap if no fit */ char *bp; /* Ignore spurious requests */ if (size == 0) return NULL; /* Adjust block size to include overhead and alignment reqs. */ if (size <= DSIZE) asize = 2*DSIZE; else asize = DSIZE * ((size + (DSIZE) + (DSIZE-1)) / DSIZE); /* Search the free list for a fit */ if ((bp = find_fit(asize)) != NULL) { place(bp, asize); return bp; } /* No fit found. Get more memory and place the block */ extendsize = MAX(asize, CHUNKSIZE); if ((bp = extend_heap(extendsize/WSIZE)) == NULL) return NULL; place(bp, asize); return bp; } /* * mm_free - Free a block */ void mm_free(void *bp) { size_t size = GET_SIZE(HDRP(bp)); PUT(HDRP(bp), PACK(size, 0)); PUT(FTRP(bp), PACK(size, 0)); coalesce(bp); } /* * mm_realloc - naive implementation of mm_realloc */ void *mm_realloc(void *ptr, size_t size) { void *newptr; size_t copySize; /* If size == 0 then this is just free, and we return NULL. */ if (size == 0) { mm_free(ptr); return 0; } /* If oldptr is NULL, then this is just malloc. */ if (ptr == NULL) { return mm_malloc(size); } newptr = mm_malloc(size); /* If realloc() fails the original block is left untouched */ if (!newptr) { return 0; } /* Copy the old data. */ copySize = GET_SIZE(HDRP(ptr)); if (size < copySize) { copySize = size; } memcpy(newptr, ptr, copySize); /* Free the old block. */ mm_free(ptr); return newptr; } /* * extend_heap - Extend heap with free block and return its block pointer */ static void *extend_heap(size_t words) { char *bp; size_t size; /* Allocate an even number of words to maintain alignment */ size = (words % 2) ? (words+1) * WSIZE : words * WSIZE; if ((long)(bp = mem_sbrk(size)) == -1) return NULL; /* Initialize free block header/footer and the epilogue header */ PUT(HDRP(bp), PACK(size, 0)); /* Free block header */ PUT(FTRP(bp), PACK(size, 0)); /* Free block footer */ PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* New epilogue header */ /* Coalesce if the previous block was free */ return coalesce(bp); } /* * coalesce - Boundary tag coalescing. Return ptr to coalesced block */ static void *coalesce(void *bp) { size_t prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))); size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp))); size_t size = GET_SIZE(HDRP(bp)); if (prev_alloc && next_alloc) { /* Case 1 */ return bp; } else if (prev_alloc && !next_alloc) { /* Case 2 */ size += GET_SIZE(HDRP(NEXT_BLKP(bp))); PUT(HDRP(bp), PACK(size, 0)); PUT(FTRP(bp), PACK(size,0)); } else if (!prev_alloc && next_alloc) { /* Case 3 */ size += GET_SIZE(HDRP(PREV_BLKP(bp))); PUT(FTRP(bp), PACK(size, 0)); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0)); bp = PREV_BLKP(bp); } else { /* Case 4 */ size += GET_SIZE(HDRP(PREV_BLKP(bp))) + GET_SIZE(FTRP(NEXT_BLKP(bp))); PUT(HDRP(PREV_BLKP(bp)), PACK(size, 0)); PUT(FTRP(NEXT_BLKP(bp)), PACK(size, 0)); bp = PREV_BLKP(bp); } return bp; } /* * find_fit - Find a fit for a block with asize bytes */ static void *find_fit(size_t asize) { void *bp; for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp)) { if (!GET_ALLOC(HDRP(bp)) && (asize <= GET_SIZE(HDRP(bp)))) { return bp; } } return NULL; /* No fit */ } /* * place - Place block of asize bytes at start of free block bp * and split if remainder would be at least minimum block size */ static void place(void *bp, size_t asize) { size_t csize = GET_SIZE(HDRP(bp)); if ((csize - asize) >= (2*DSIZE)) { PUT(HDRP(bp), PACK(asize, 1)); PUT(FTRP(bp), PACK(asize, 1)); bp = NEXT_BLKP(bp); PUT(HDRP(bp), PACK(csize-asize, 0)); PUT(FTRP(bp), PACK(csize-asize, 0)); } else { PUT(HDRP(bp), PACK(csize, 1)); PUT(FTRP(bp), PACK(csize, 1)); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值