专业汉语词典知识平台,分享汉字词语知识、历史文学知识解答!

励北网
励北网

malloc函数的用法,malloc内存分配原理

来源:小易整编  作者:小易  发布时间:2023-02-03 04:49
摘要:malloc函数的用法,malloc内存分配原理,任何一个用过或学过C的人对malloc都不会陌生。大家都知道malloc可以分配一段连续的内存空间,并且在不再使用时可以通过free释放掉。但是,许多程序员对malloc背后的事情并不熟悉,...

3.2.5 malloc的实现

有了上面的代码,我们可以利用它们整合成一个简单但初步可用的malloc。注意首先我们要定义个block链表的头first_block,初始化为NULL;另外,我们需要剩余空间至少有BLOCK_SIZE + 8才执行分裂操作。

由于我们希望malloc分配的数据区是按8字节对齐,所以在size不为8的倍数时,我们需要将size调整为大于size的最小的8的倍数:

size_talign8(size_ts){if(s&0x7==0)returns;return((s>>3)+1)<<3;}

#defineBLOCK_SIZE24void*first_block=NULL;/*otherfunctions...*/void*malloc(size_tsize){t_blockb,last;size_ts;/*对齐地址*/s=align8(size);if(first_block){/*查找合适的block*/last=first_block;b=find_block(&last,s);if(b){/*如果可以,则分裂*/if((b->size-s)>=(BLOCK_SIZE+8))split_block(b,s);b->free=0;}else{/*没有合适的block,开辟一个新的*/b=extend_heap(last,s);if(!b)returnNULL;}}else{b=extend_heap(NULL,s);if(!b)returnNULL;first_block=b;}returnb->data;}

3.2.6 calloc的实现

有了malloc,实现calloc只要两步:

  1. malloc一段内存

  2. 将数据区内容置为0

由于我们的数据区是按8字节对齐的,所以为了提高效率,我们可以每8字节一组置0,而不是一个一个字节设置。我们可以通过新建一个size_t指针,将内存区域强制看做size_t类型来实现。

void*calloc(size_tnumber,size_tsize){size_t*new;size_ts8,i;new=malloc(number*size);if(new){s8=align8(number*size)>>3;for(i=0;i<s8;i++)new[i]=0;}returnnew;}

3.2.7 free的实现

free的实现并不像看上去那么简单,这里我们要解决两个关键问题:

  1. 如何验证所传入的地址是有效地址,即确实是通过malloc方式分配的数据区首地址

  2. 如何解决碎片问题

首先我们要保证传入free的地址是有效的,这个有效包括两方面:

  • 地址应该在之前malloc所分配的区域内,即在first_block和当前break指针范围内

  • 这个地址确实是之前通过我们自己的malloc分配的

第一个问题比较好解决,只要进行地址比较就可以了,关键是第二个问题。

这里有两种解决方案:一是在结构体内埋一个magic number字段,free之前通过相对偏移检查特定位置的值是否为我们设置的magic number,另一种方法是在结构体内增加一个magic pointer,这个指针指向数据区的第一个字节(也就是在合法时free时传入的地址),我们在free前检查magic pointer是否指向参数所指地址。这里我们采用第二种方案:

首先我们在结构体中增加magic pointer(同时要修改BLOCK_SIZE):

typedefstructs_block*t_block;structs_block{size_tsize;/*数据区大小*/t_blocknext;/*指向下个块的指针*/intfree;/*是否是空闲块*/intpadding;/*填充4字节,保证meta块长度为8的倍数*/void*ptr;/*Magicpointer,指向data*/chardata[1]/*这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta*/};

然后我们定义检查地址合法性的函数:

t_blockget_block(void*p){char*tmp;tmp=p;return(p=tmp-=BLOCK_SIZE);}intvalid_addr(void*p){if(first_block){if(p>first_block&&p

当多次malloc和free后,整个内存池可能会产生很多碎片block,这些block很小,经常无法使用,甚至出现许多碎片连在一起,虽然总体能满足某此malloc要求,但是由于分割成了多个小block而无法fit,这就是碎片问题。

一个简单的解决方式时当free某个block时,如果发现它相邻的block也是free的,则将block和相邻block合并。为了满足这个实现,需要将s_block改为双向链表。修改后的block结构如下:

typedefstructs_block*t_block;structs_block{size_tsize;/*数据区大小*/t_blockprev;/*指向上个块的指针*/t_blocknext;/*指向下个块的指针*/intfree;/*是否是空闲块*/intpadding;/*填充4字节,保证meta块长度为8的倍数*/void*ptr;/*Magicpointer,指向data*/chardata[1]/*这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta*/};

合并方法如下:

t_blockfusion(t_blockb){if(b->next&&b->next->free){b->size+=BLOCK_SIZE+b->next->size;b->next=b->next->next;if(b->next)b->next->prev=b;}returnb;}

有了上述方法,free的实现思路就比较清晰了:首先检查参数地址的合法性,如果不合法则不做任何事;否则,将此block的free标为1,并且在可以的情况下与后面的block进行合并。

如果当前是最后一个block,则回退break指针释放进程内存,如果当前block是最后一个block,则回退break指针并设置first_block为NULL。实现如下:

voidfree(void*p){t_blockb;if(valid_addr(p)){b=get_block(p);b->free=1;if(b->prev&&b->prev->free)b=fusion(b->prev);if(b->next)fusion(b);else{if(b->prev)b->prev->prev=NULL;elsefirst_block=NULL;brk(b);}}}3.2.8 realloc的实现

为了实现realloc,我们首先要实现一个内存复制方法。如同calloc一样,为了效率,我们以8字节为单位进行复制:

voidcopy_block(t_blocksrc,t_blockdst){size_t*sdata,*ddata;size_ti;sdata=src->ptr;ddata=dst->ptr;for(i=0;(i*8)

然后我们开始实现realloc。一个简单(但是低效)的方法是malloc一段内存,然后将数据复制过去。但是我们可以做的更高效,具体可以考虑以下几个方面:

  • 如果当前block的数据区大于等于realloc所要求的size,则不做任何操作

  • 如果新的size变小了,考虑split

  • 如果当前block的数据区不能满足size,但是其后继block是free的,并且合并后可以满足,则考虑做合并

下面是realloc的实现:

void*realloc(void*p,size_tsize){size_ts;t_blockb,new;void*newp;if(!p)/*根据标准库文档,当p传入NULL时,相当于调用malloc*/returnmalloc(size);if(valid_addr(p)){s=align8(size);b=get_block(p);if(b->size>=s){if(b->size-s>=(BLOCK_SIZE+8))split_block(b,s);}else{/*看是否可进行合并*/if(b->next&&b->next->free&&(b->size+BLOCK_SIZE+b->next->size)>=s){fusion(b);if(b->size-s>=(BLOCK_SIZE+8))split_block(b,s);}else{/*新malloc*/newp=malloc(s);if(!newp)returnNULL;new=get_block(newp);

copy_block(b,new);free(p);return(newp);}}return(p);}returnNULL;}

3.3 遗留问题和优化

以上是一个较为简陋,但是初步可用的malloc实现。还有很多遗留的可能优化点,例如:

  • 同时兼容32位和64位系统

  • 在分配较大快内存时,考虑使用mmap而非sbrk,这通常更高效

  • 可以考虑维护多个链表而非单个,每个链表中的block大小均为一个范围内,例如8字节链表、16字节链表、24-32字节链表等等。此时可以根据size到对应链表中做分配,可以有效减少碎片,并提高查询block的速度

  • 可以考虑链表中只存放free的block,而不存放已分配的block,可以减少查找block的次数,提高效率

还有很多可能的优化,这里不一一赘述。下面附上一些参考文献,有兴趣的同学可以更深入研究。

4 其它参考

  1. 这篇文章大量参考了A malloc Tutorial[7],其中一些图片和代码直接引用了文中的内容,这里特别指出

  2. Computer Systems: A Programmer's Perspective, 2/E[8]一书有许多值得参考的地方

  3. 关于Linux的虚拟内存模型,Anatomy of a Program in Memory[9]是很好的参考资料,另外作者还有一篇How the Kernel Manages Your Memory[10]对于Linux内核中虚拟内存管理的部分有很好的讲解

  4. 对于真实世界的malloc实现,可以参考glibc的实现[11]

  5. 本文写作过程中大量参考了维基百科[12],再次感谢这个伟大的网站,并且呼吁大家在手头允许的情况下可以适当捐助维基百科,帮助这个造福人类的系统运行下去


本文地址:百科问答频道 https://www.neebe.cn/wenda/903029_3.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!

共3页 1 2 3 当前是最后一页

百科问答
小编:小易整编
相关文章相关阅读
  • Excel中countif函数的使用方法

    Excel中countif函数的使用方法

    Excel中有很多函数,很多都可以为我们大大减少计算的时间,一步就得出结果,很多朋友在使用countif函数时,会出现一些错误导致不成功,我们这就来给你详细讲讲countif函数是应该如何使用的。countif函数的含义...

  • SUBTOTAL函数怎么用?

    SUBTOTAL函数怎么用?

    操作方法01隐藏行汇总方法:在目标单元格输入公式:=SUBTOTAL(109,C3:C9)。02筛选汇总。方法:在目标单元格输入公式:=SUBTOTAL...

  • 函数库是什么意思?

    函数库是什么意思?

    函数库是指由一组编写好的、结构化的可执行函数组成的库文件,其中的函数可以增加程序的通用功能以提升程序的运行效率,节省开发时间并提高软件的质量。函数库更常用于缩短编程时间、提供程序实现通用功能以及用于程序编写中特定功能的实现。函数库有帮助程...

  • 用数学画图软件——Graph绘制函数图形

    用数学画图软件——Graph绘制函数图形

    Graph是一款开源类的绘制函数图像软件。它不仅能根据函数绘制其图像,还能够绘制曲线上的切线、法线和阴影等。除了绘制功能,它还具有计算功能,其中包括曲线长度、面积等的计算。下面我来给大家介绍一下如何使用Graph绘制函数图像。操作方法...

  • Excel SLOPE函数的使用方法

    Excel SLOPE函数的使用方法

    在数学中SLOPE是斜率的意思,Excel中的SLOPE函数也是一个计算斜率的函数。请诸位和我一起学习——SLOPE函数。操作方法01SLOPE函数的功能把已知的自变量和因变量作为数据点,计算线性回...

  • Excel之MODE函数使用方法

    Excel之MODE函数使用方法

    MODE返回的数组或数据区域中出现频率最高或重复出现次数最多的值。此函数已被替换MODE.MULT函数和MODE.SNGL函数。操作方法01打开Excel,将测试使用的数据复制到表格中,如下图。...

  • 数学画图软件函数哪个好用 函数生成图像软件推荐

    数学画图软件函数哪个好用 函数生成图像软件推荐

    对于科技发达的互联网时代,很多学生是可以借用软件的方式来帮助自己快速的完成作业,接下来就简单的给大家分享下数学画图软件函数哪个好用,这次的合集里边会有几款非常经典的佳作分享给大家,通过时间的证明足以看到它们的优越性,感兴趣的话可以跟小编自己...

  • 反函数公式掌握,提高数学水平(应对复杂计算)

    反函数公式掌握,提高数学水平(应对复杂计算)

    反函数公式掌握,提高数学水平(应对复杂计算)反函数公式是高中数学中比较重要的概念之一,掌握反函数公式不仅可以提高数学水平,还可以帮助我们应对各种复杂计算。反函数公式是指将函数f(x)的自变量x和因变量y对调,得到一个新的函数g(y),称为f...

  • 周排行
  • 月排行
  • 年排行

精彩推荐