专业游戏门户,分享手游网游单机游戏百科知识攻略!

嗨游网
嗨游网

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

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

2.2.2 Heap内存模型

一般来说,malloc所申请的内存主要从Heap区域分配(本文不考虑通过mmap申请大块内存的情况)。

由上文知道,进程所面对的虚拟内存地址空间,只有按页映射到物理内存地址,才能真正使用。受物理存储容量限制,整个堆虚拟内存空间不可能全部映射到实际的物理内存。Linux对堆的管理示意如下:

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

Linux维护一个break指针,这个指针指向堆空间的某个地址。从堆起始地址到break之间的地址空间为映射好的,可以供进程访问;而从break往上,是未映射的地址空间,如果访问这段空间则程序会报错。

2.2.3 brk与sbrk

由上文知道,要增加一个进程实际的可用堆大小,就需要将break指针向高地址移动。Linux通过brk和sbrk系统调用操作break指针。两个系统调用的原型如下:

intbrk(void*addr);void*sbrk(intptr_tincrement);

brk将break指针直接设置为某个地址,而sbrk将break从当前位置移动increment所指定的增量。brk在执行成功时返回0,否则返回-1并设置errno为ENOMEM;sbrk成功时返回break移动之前所指向的地址,否则返回(void *)-1。

一个小技巧是,如果将increment设置为0,则可以获得当前break的地址。

另外需要注意的是,由于Linux是按页进行内存映射的,所以如果break被设置为没有按页大小对齐,则系统实际上会在最后映射一个完整的页,从而实际已映射的内存空间比break指向的地方要大一些。但是使用break之后的地址是很危险的(尽管也许break之后确实有一小块可用内存地址)。

2.2.4 资源限制与rlimit

系统对每一个进程所分配的资源不是无限的,包括可映射的内存空间,因此每个进程有一个rlimit表示当前进程可用的资源上限。这个限制可以通过getrlimit系统调用得到,下面代码获取当前进程虚拟内存空间的rlimit:

intmain(){structrlimit*limit=(structrlimit*)malloc(sizeof(structrlimit));getrlimit(RLIMIT_AS,limit);printf("softlimit:%ld,hardlimit:%ld\n",limit->rlim_cur,limit->rlim_max);}

其中rlimit是一个结构体:

structrlimit{rlim_trlim_cur;/*Softlimit*/rlim_trlim_max;/*Hardlimit(ceilingforrlim_cur)*/};

每种资源有软限制和硬限制,并且可以通过setrlimit对rlimit进行有条件设置。其中硬限制作为软限制的上限,非特权进程只能设置软限制,且不能超过硬限制。

3 实现malloc3.1 玩具实现

在正式开始讨论malloc的实现前,我们可以利用上述知识实现一个简单但几乎没法用于真实的玩具malloc,权当对上面知识的复习:

/*一个玩具malloc*/#include

这个malloc每次都在当前break的基础上增加size所指定的字节数,并将之前break的地址返回。这个malloc由于对所分配的内存缺乏记录,不便于内存释放,所以无法用于真实场景。

3.2 正式实现

下面严肃点讨论malloc的实现方案。

3.2.1 数据结构

首先我们要确定所采用的数据结构。一个简单可行方案是将堆内存空间以块(Block)的形式组织起来,每个块由meta区和数据区组成,meta区记录数据块的元信息(数据区大小、空闲标志位、指针等等),数据区是真实分配的内存区域,并且数据区的第一个字节地址即为malloc返回的地址。

可以用如下结构体定义一个block:

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

由于我们只考虑64位机器,为了方便,我们在结构体最后填充一个int,使得结构体本身的长度为8的倍数,以便内存对齐。示意图如下:

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

3.2.2 寻找合适的block

现在考虑如何在block链中查找合适的block。一般来说有两种查找算法:

  • First fit:从头开始,使用第一个数据区大小大于要求size的块所谓此次分配的块

  • Best fit:从头开始,遍历所有块,使用数据区大小大于size且差值最小的块作为此次分配的块

两种方法各有千秋,best fit具有较高的内存使用率(payload较高),而first fit具有更好的运行效率。这里我们采用first fit算法。

/*Firstfit*/t_blockfind_block(t_block*last,size_tsize){t_blockb=first_block;while(b&&!(b->free&&b->size>=size)){*last=b;b=b->next;}returnb;}

find_block从frist_block开始,查找第一个符合要求的block并返回block起始地址,如果找不到这返回NULL。这里在遍历时会更新一个叫last的指针,这个指针始终指向当前遍历的block。这是为了如果找不到合适的block而开辟新block使用的,具体会在接下来的一节用到。

3.2.3 开辟新的block

如果现有block都不能满足size的要求,则需要在链表最后开辟一个新的block。这里关键是如何只使用sbrk创建一个struct:

#defineBLOCK_SIZE24/*由于存在虚拟的data字段,sizeof不能正确计算meta长度,这里手工设置*/t_blockextend_heap(t_blocklast,size_ts){t_blockb;b=sbrk(0);if(sbrk(BLOCK_SIZE+s)==(void*)-1)returnNULL;b->size=s;b->next=NULL;if(last)last->next=b;b->free=0;returnb;}

3.2.4 分裂block

First fit有一个比较致命的缺点,就是可能会让很小的size占据很大的一块block,此时,为了提高payload,应该在剩余数据区足够大的情况下,将其分裂为一个新的block,示意如下:

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

实现代码:

voidsplit_block(t_blockb,size_ts){t_blocknew;new=b->data+s;new->size=b->size-s-BLOCK_SIZE;new->next=b->next;new->free=1;b->size=s;b->next=new;}


本文地址:IT问答频道 https://www.eeeoo.cn/itwenda/903029_2.html,嗨游网一个专业手游免费下载攻略知识分享平台,本站部分内容来自网络分享,不对内容负责,如有涉及到您的权益,请联系我们删除,谢谢!


IT问答
小编:小嗨整编
相关文章相关阅读
  • 办公软件快捷键大全表(办公软件函数公式大全)

    办公软件快捷键大全表(办公软件函数公式大全)

    办公软件快捷键大全表(办公软件函数公式大全)办公软件快捷键大全表1.Alt系列2.Ctrl+数字3.Ctrl+Shift4.Shift系列办公软件函数公式大全1、Ctrl+字母Ctrl+A全选Ctrl+P打印Ctrl+C复制Ctrl+V粘贴...

  • c语言的输入函数有哪些

    c语言的输入函数有哪些

    c语言的输入函数有:1、scanf()函数、从标准输入stdin读取格式化输入;2、getchar()函数,从标准输入stdin获取一个字符;3、gets()函数,从标准输入stdin读取一行;4、getch()函数,从stdin流中读取字...

  • 什么是构造函数?详解JavaScript中的构造函数

    什么是构造函数?详解JavaScript中的构造函数

    作为原型和原型链的基础,先了解清楚构造函数以及它的执行过程才能更好地帮助我们学习原型和原型链的知识。本篇文章带大家详细了解一下javascript中的构造函数,介绍一下怎么利用构造函数创建一个js对象,希望对大家有所帮助!一个普通的函数被用...

  • Excel函数学习之CHOOSE函数 vs IF函数

    Excel函数学习之CHOOSE函数 vs IF函数

    如果Excel函数圈也有江湖,那CHOOSE函数绝对算得上扫地僧。它不如IF函数那般威震江湖,但它的本领却更胜一筹。今天小花就带大家好好见识一下被大多数人冷遇的CHOOSE函数!   CHOOSE函数使用index_num返回数值参数列...

  • Matlab中length函数怎么用

    Matlab中length函数怎么用

    在matlab中,length函数用于返回向量、数组或字符串中的元素个数。以下是length函数的一些用法示例:1、返回向量中的元素个数:v = [1, 2, 3, 4, 5];  numElements = length(v); % 结果...

  • mysql列转行函数是什么

    mysql列转行函数是什么

    在mysql中,列转行函数是“group_concat()”函数;该函数用于将非空列值按照分组条件进行合并并最终返回,如果其中有空值则返回的结果是空,语法为“selectgroup_concat(name separator';')列...

  • excel求差值用什么函数

    excel求差值用什么函数

    在excel中求差值是没有专门的函数,excel求差值的方法是:首先打开excel工作表;然后在f7单元格内输入“=d7-e7”公式;最后按回车即可得到两个数之间的差值即可。本文操作环境:Windows7系统、DellG3电脑、Micro...

  • Excel Mid函数的使用方法

    Excel Mid函数的使用方法

    在Excel中,提取指定长度的字符有两个函数,分别为Mid函数和Midb函数,前者用于提取指定长度的字符个数,后者用于提取指定长度的字节个数。用Mid函数提取时,无论是汉字、字母还是数字都算一个字符;用Midb函数提取时,汉字算两个字节...

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

精彩推荐