ptmalloc源码分析 - realloc()函数的实现(11)

目录

一、步骤1-判断边界情况,realloc也可以执行malloc和free的功能

二、步骤2-原chunk如果MMAP方式分配,申请新内存并拷贝实现

三、步骤3-非MMAP方式分配,则_int_realloc进行合并/裁剪等实现

1. _int_realloc函数:老chunk足够大,则裁剪

2. _int_realloc函数:尝试从Top chunk上进行扩展

3. _int_realloc函数:尝试合并nextchunk空间切割

4. _int_realloc函数:调用_int_malloc函数分配新内存

5. _int_realloc函数:裁剪返回Userchunk释放Remainder chunk

四、步骤4-如果_int_realloc分配失败,则尝试__libc_malloc分配一次


本章节主要讲解一下realloc()函数的实现。free函数的入口函数:__libc_realloc,该入口函数也在malloc.c的文件中。

前几章我们讲解了malloc的实现和free的实现,基本了解了ptmalloc是如何来管理内存结构的。此章节主要讲一下realloc()函数。该函数意思就是重置一个内存的大小。基本有以下几个步骤来实现该函数逻辑:

  1. 步骤1:判断边界情况,realloc也可以执行malloc和free的功能
  2. 步骤2:原chunk如果MMAP方式分配,则新申请内存并拷贝方式实现
  3. 步骤3:非MMAP方式分配,则_int_realloc进行合并/裁剪等方式实现
  4. 步骤4:如果_int_realloc分配失败,则尝试__libc_malloc分配一次

一、步骤1-判断边界情况,realloc也可以执行malloc和free的功能


边界情况主要有两种:

  1. 如果bytes为0的情况,相当于执行一次free操作
  2. 如果老的指针为空的情况下,相当于执行一次malloc操作

通过checked_request2size函数,检查bytes是否在分配合法区间内,并且将bytes进行对齐,最终得到对齐的nb大小的内存申请容量。

/*** 重新设置内存大小*/
void *
__libc_realloc (void *oldmem, size_t bytes)
{mstate ar_ptr;INTERNAL_SIZE_T nb;    //字节对齐后需要分配的大小     /* padded request size */void *newp;      //返回新的chunk地址        /* chunk to return */void *(*hook) (void *, size_t, const void *) =atomic_forced_read (__realloc_hook);if (__builtin_expect (hook != NULL, 0))return (*hook)(oldmem, bytes, RETURN_ADDRESS (0));//如果bytes==0 则相当于free,调用__libc_free函数
#if REALLOC_ZERO_BYTES_FREESif (bytes == 0 && oldmem != NULL){__libc_free (oldmem); return 0;}
#endif/* realloc of null is supposed to be same as malloc *//* 如果老的内存指针为空,则相当于重新分配一块内存,调用__libc_malloc函数 */if (oldmem == 0)return __libc_malloc (bytes);/* chunk corresponding to oldmem */const mchunkptr oldp = mem2chunk (oldmem); //获取老的chunk指针地址/* its size */const INTERNAL_SIZE_T oldsize = chunksize (oldp); //获取来的chunk的size/* 判断chunk是否为MMAP分配方式 IS_MMAPPED*/if (chunk_is_mmapped (oldp))ar_ptr = NULL; //分配区NULLelse{MAYBE_INIT_TCACHE ();ar_ptr = arena_for_chunk (oldp); //获取分配区}/* Little security check which won't hurt performance: the allocatornever wrapps around at the end of the address space.  Thereforewe can exclude some size values which might appear here byaccident or by "design" from some intruder.  We need to bypassthis check for dumped fake mmap chunks from the old main arenabecause the new malloc may provide additional alignment.  */if ((__builtin_expect ((uintptr_t) oldp > (uintptr_t) -oldsize, 0)|| __builtin_expect (misaligned_chunk (oldp), 0))&& !DUMPED_MAIN_ARENA_CHUNK (oldp))malloc_printerr ("realloc(): invalid pointer");//检查bytes是否在合法区间内,最大小于<2147483647//并调用request2size函数,将bytes进行对齐,最终得到对齐的nb大小的内存申请容量if (!checked_request2size (bytes, &nb)) //XXX{__set_errno (ENOMEM);return NULL;}

二、步骤2-原chunk如果MMAP方式分配,申请新内存并拷贝实现


这里有几个逻辑:

  1. 如果是伪造的主分区的MMAP的chunk,则通过__lib_malloc函数申请一块新内存,将老数据拷贝memcpy到新内存上,老内存不free
  2. 如果是标准MMAP的chunk,则通过__lib_malloc函数申请一块新内存,将老数据拷贝到新内存上,最后需要调用munmap_chunk销毁这个chunk
  3. 如果老的内存块数据减去一个SIZE_SZ,还能比新的对齐后的内存块大,则直接返回老内存,不需要做任何改变
 //如果是MMAP的方式if (chunk_is_mmapped (oldp)){/* If this is a faked mmapped chunk from the dumped main arena,always make a copy (and do not free the old chunk).  *///如果是伪造的主分区的一个MMAP的chunk//重新申请一个内存块,并将老的内存块数据拷贝到新的内存块上if (DUMPED_MAIN_ARENA_CHUNK (oldp)){/* Must alloc, copy, free. */void *newmem = __libc_malloc (bytes);if (newmem == 0)return NULL;/* Copy as many bytes as are available from the old chunkand fit into the new size.  NB: The overhead for fakedmmapped chunks is only SIZE_SZ, not 2 * SIZE_SZ as forregular mmapped chunks.  */if (bytes > oldsize - SIZE_SZ)bytes = oldsize - SIZE_SZ;memcpy (newmem, oldmem, bytes); //内存拷贝return newmem;}void *newmem;#if HAVE_MREMAPnewp = mremap_chunk (oldp, nb);if (newp)return chunk2mem (newp);
#endif/* Note the extra SIZE_SZ overhead. *///如果老的内存块的size减去SIZE_SZ后,都比新申请的大,则什么都不做,返回老的指针if (oldsize - SIZE_SZ >= nb)return oldmem;                         /* do nothing *//* Must alloc, copy, free. */newmem = __libc_malloc (bytes); //新申请内存if (newmem == 0)return 0;              /* propagate failure */memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ); //拷贝oldsize为chunk的大小,2 * SIZE_SZ存储chunk的结构munmap_chunk (oldp); //MMAP分配的,调用munmap_chunk所以释放老的内存地址return newmem;}

三、步骤3-非MMAP方式分配,则_int_realloc进行合并/裁剪等实现


单线程情况下直接调用_int_realloc函数。

多线程情况下需要加锁然后调用_int_realloc实现。

 /* 单线程情况 */if (SINGLE_THREAD_P){newp = _int_realloc (ar_ptr, oldp, oldsize, nb); //直接调用_int_realloc核心函数assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||ar_ptr == arena_for_chunk (mem2chunk (newp)));return newp; //返回新的指针地址}/* 多线程模式下,非MMAP分配方式 */__libc_lock_lock (ar_ptr->mutex); //加锁newp = _int_realloc (ar_ptr, oldp, oldsize, nb); //调用_int_realloc核心函数__libc_lock_unlock (ar_ptr->mutex); //解锁assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||ar_ptr == arena_for_chunk (mem2chunk (newp)));

1. _int_realloc函数:老chunk足够大,则裁剪


老的chunk内存,大于新分配的,空间足够,则可以进行分割。将老的内存切割成2个chunk,一个返回给用户端,一个放入bins上进行管理。

oldsize需要大于nb,nb是经过checked_request2size函数进行字节对齐的内存大小。

/*** realloc核心函数*/
void*
_int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,INTERNAL_SIZE_T nb)
{mchunkptr        newp;            /* chunk to return */INTERNAL_SIZE_T  newsize;         /* its size */void*          newmem;          /* corresponding user mem */mchunkptr        next;            /* next contiguous chunk after oldp */mchunkptr        remainder;       /* extra space at end of newp */unsigned long    remainder_size;  /* its size *//* oldmem size */if (__builtin_expect (chunksize_nomask (oldp) <= 2 * SIZE_SZ, 0)|| __builtin_expect (oldsize >= av->system_mem, 0))malloc_printerr ("realloc(): invalid old size");check_inuse_chunk (av, oldp); //检查使用状态/* All callers already filter out mmap'ed chunks.  */assert (!chunk_is_mmapped (oldp));next = chunk_at_offset (oldp, oldsize); //获取下一个chunkINTERNAL_SIZE_T nextsize = chunksize (next); //下一个chunk的sizeif (__builtin_expect (chunksize_nomask (next) <= 2 * SIZE_SZ, 0)|| __builtin_expect (nextsize >= av->system_mem, 0))malloc_printerr ("realloc(): invalid next size");//老的chunk内存,大于新分配的,空间足够,则可以进行分割if ((unsigned long) (oldsize) >= (unsigned long) (nb)){/* already big enough; split below */newp = oldp;newsize = oldsize;}

2. _int_realloc函数:尝试从Top chunk上进行扩展


如果 nextchunk 是Topchunk ,并且新的chunk大小大于申请的大小,则直接合并Topchunk进行切割。 

调整av->top的值,设置Topchunk 标记物理相邻前一个chunk为使用中PREV_INUSE

  else{/* Try to expand forward into top *//* 如果 nextchunk 是Topchunk ,并且新的chunk大小大于申请的大小,则直接合并Topchunk进行切割*/if (next == av->top &&(unsigned long) (newsize = oldsize + nextsize) >=(unsigned long) (nb + MINSIZE)){set_head_size (oldp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0)); //设置头av->top = chunk_at_offset (oldp, nb); //调整av->top的值set_head (av->top, (newsize - nb) | PREV_INUSE); //设置Topchunk 标记物理相邻前一个chunk为使用中check_inuse_chunk (av, oldp);return chunk2mem (oldp); //chunk指针地址 转到 内存指针地址}

3. _int_realloc函数:尝试合并nextchunk空间切割


如果nextchunk不是Top chunk ,并且next为空闲状态,则将nextchunk进行合并。合并完了之后,调用unlink_chunk,解除nextchunk的bins上的关系。

      /* Try to expand forward into next chunk;  split off remainder below *//* 如果nextchunk不是Top chunk ,并且next为空闲状态,则将nextchunk进行合并*/else if (next != av->top &&!inuse (next) &&(unsigned long) (newsize = oldsize + nextsize) >=(unsigned long) (nb)){newp = oldp;unlink_chunk (av, next); //解除空闲chunk的bins上的关系}

4. _int_realloc函数:调用_int_malloc函数分配新内存


首先调用_int_malloc函数,直接分配一个新的newmem。通过newmem,获取得到当前chunk的大小和chunk地址。如果新分配的正好是nextchunk,则直接合并到一起。如果不是,则释放老内存,拷贝就内存数据到新内存上。 

      /* allocate, copy, free *//* nextchunk为使用中,或其它情况 */else{newmem = _int_malloc (av, nb - MALLOC_ALIGN_MASK); //调用_int_malloc新分配一块内存if (newmem == 0)return 0; /* propagate failure */newp = mem2chunk (newmem); //返回新的指针地址newsize = chunksize (newp); //返回新的chunk的size/*Avoid copy if newp is next chunk after oldp.*//* 如果新分配的物理地址是老chunk的下一个chunk */if (newp == next){newsize += oldsize; //调整size,合并nextchunknewp = oldp;}else{memcpy (newmem, chunk2mem (oldp), oldsize - SIZE_SZ); //开始进行内存拷贝_int_free (av, oldp, 1); //释放老内存check_inuse_chunk (av, newp);return chunk2mem (newp); //返回内存地址}}}

5. _int_realloc函数:裁剪返回Userchunk释放Remainder chunk


 老的内存块经过合并后,足够大,则需要进行裁剪操作。如果进行裁剪前,发现没有足够的空间了,则不裁剪了,设置一下nextchunk的标记位就行了;如果有足够空间,则裁剪出Remainder chunk,并通过_int_free函数,将裁剪出来的剩余chunk释放到bins上管理。

  /* If possible, free extra space in old or extended chunk */assert ((unsigned long) (newsize) >= (unsigned long) (nb));/* 老的内存块经过合并后,足够大,则需要进行裁剪操作 */remainder_size = newsize - nb; //裁剪剩余的内存/* 进行裁剪前,如果没有足够的空间了,则不裁剪了 */if (remainder_size < MINSIZE)   /* not enough extra to split off */{set_head_size (newp, newsize | (av != &main_arena ? NON_MAIN_ARENA : 0));set_inuse_bit_at_offset (newp, newsize); //下一个chunk标记前一个chunk使用中}else   /* split remainder */{remainder = chunk_at_offset (newp, nb); //获取裁剪的chunkset_head_size (newp, nb | (av != &main_arena ? NON_MAIN_ARENA : 0));set_head (remainder, remainder_size | PREV_INUSE |(av != &main_arena ? NON_MAIN_ARENA : 0)); //设置裁剪chunk的状态/* Mark remainder as inuse so free() won't complain */set_inuse_bit_at_offset (remainder, remainder_size); //_int_free (av, remainder, 1); //释放裁剪后的chunk}check_inuse_chunk (av, newp);return chunk2mem (newp); //返回内存
}

四、步骤4-如果_int_realloc分配失败,则尝试__libc_malloc分配一次


如果调用_int_realloc失败,则尝试__libc_malloc,强制重新分配一次。

  /* 分配失败 ,则调用__libc_malloc进行重试一次*/if (newp == NULL){/* Try harder to allocate memory in other arenas.  */LIBC_PROBE (memory_realloc_retry, 2, bytes, oldmem);newp = __libc_malloc (bytes);/* 分配成功,调用memcpy拷贝数据, _int_free释放老的chunk*/if (newp != NULL){memcpy (newp, oldmem, oldsize - SIZE_SZ);_int_free (ar_ptr, oldp, 0);}}return newp;
}
libc_hidden_def (__libc_realloc)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/142474.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统化思考,从初级到高级书单推荐

用思考工具进行系统思考&#xff0c;解决复杂问题&#xff0c;成为某个领域的高手&#xff0c;下面这几本书就是补充你脑海的系统思考的工具&#xff0c;一定要保存。 《简单的逻辑学》 作者&#xff1a;麦克伦尼 一切的系统源自于逻辑&#xff0c;如果你没有逻辑分析的能力&…

jenkins容器内配置python项目运行环境(Python3.7.3)

目录 1.查看启动的容器2.进入jenkins容器内部3.使用wget&#xff1a;提示没有wget命令4.查看jenkins容器系统版本5.换成国内源&#xff08;阿里&#xff09;5.更新apt-get6.安装wget7.创建python存放目录8.下载python9.解压10.安装依赖11.运行脚本configure12.make编译make ins…

JavaWeb 学习笔记 11:JDBC

JavaWeb 学习笔记 11&#xff1a;JDBC 1.简介 JDBC 是一个 Java 为接入不同类型的数据库定义的一个数据库连接和执行 SQL 的 API。 可以用下图表示&#xff1a; 图中的具体数据库的驱动实际上就是数据库厂商提供的 JDBC 接口的实现类。 2.快速开始 用 Maven 创建一个简单的…

使用 PyTorch 的计算机视觉简介 (5/6)

一、说明 本文主要介绍CNN中在pytorch的实现&#xff0c;其中VGG16网络&#xff0c;数据集来源&#xff0c;以及训练过程&#xff0c;模型生成和存储&#xff0c;模型调入等。 二、预训练模型和迁移学习 训练 CNN 可能需要大量时间&#xff0c;并且该任务需要大量数据。但是&am…

Python+Django前后端分离

程序示例精选 PythonDjango前后端分离 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonDjango前后端分离》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应…

安防视频/集中云存储平台EasyCVR(V3.3)部分通道显示离线该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

c语言练习67:写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

写一个宏&#xff0c;可以将一个整数的二进制位的奇数位和偶数位交换。 #define SwapIntBit(n) (((n) & 0x55555555) << 1 | ((n) & 0xaaaaaaaa) >> 1) 交换奇偶位&#xff0c;需要先分别拿出奇偶位。既然是宏&#xff0c;分别拿出用循环不是很现实&…

消息中间件(二)——kafka

文章目录 Apache Kafka综述什么是消息系统&#xff1f;点对点消息类型发布-订阅消息类型 什么是Kafka?优点关键术语Kafka基本原理用例 Apache Kafka综述 在大数据中&#xff0c;会使用到大量的数据。面对这些海量的数据&#xff0c;我们一是需要做到能够收集这些数据&#xf…

Vue3 中的虚拟DOM、 h() 函数,渲染函数,渲染器知识点一网打尽!

在平常开发阶段我们总是分不清虚拟 DOM、 h() 函数、渲染函数和渲染器的知识。笔者在翻阅相关文档之后&#xff0c;总结了下面这些知识点。 h() 函数用于创建虚拟 DOM&#xff0c;渲染函数的作用就是返回虚拟 DOM。因此&#xff0c;我们可以在渲染函数中使用 h() 创建虚拟 DOM…

【KingFusion】如何在3D场景实现流水效果

哈喽&#xff0c;大家好,我是雷工&#xff01; 在项目过程中&#xff0c;经常会涉及到实现管道水流动效果&#xff0c;此篇记录在KingFusion中的3D场景实现水流效果。 以下为简单流水效果的样例&#xff0c; 一、效果展示 当点击水泵&#xff0c;水泵启动&#xff0c;显示流水…

知网G4期刊-基础教育论坛-如何投稿?

《基础教育论坛》知网 3版5000字符 24年上半年刊期&#xff0c;可收中小学基础教育&#xff0c;幼儿教育等教育全科文章。 《基础教育论坛》主要刊登有关教育教学理论探讨及课程改革、教学改革、考试改革研究等方面的文章&#xff0c;为广大基础教育工作者提供学术交流的…

卫星图像应用 - 洪水检测 数据预处理

执行环境&#xff1a;Google Colab !pip install basemap下载basemap用于在地图上绘制2D数据。 import os import json import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import matplotlib.gridspec as gridspec from mpl_toolkits.ba…

逻辑像素与物理像素引发学习型探索

文章目录 目的关于像素从像素到分辨率DP(设备像素&#xff09;- 物理像素DIP(逻辑像素&#xff09;- 设备独立像素CSS 像素屏幕特性 DRP&#xff08;设备像素比&#xff09;PPI(Pixels Per Inch ) - 像素密度屏幕像素密度PPI 目的 做一个前端或或者产品开发者&#xff0c; 在涉…

MySQL基础篇-约束

目录 1.约束概述 2.分类 3.测试user表的约束情况 主键约束 非空约束及唯一约束 检查约束 默认约束 4.外键约束 外键约束的语法 外键约束的删除/更新行为 小结 1.约束概述 MySQL约束&#xff08;Constraints&#xff09;是用于确保表中数据完整性和一致性的规则。它们定…

ArrayList 的扩容机制

ArrayList扩容的本质就是计算出新的扩容数组的size后实例化&#xff0c;并将原有数组内容复制到新数组中去。默认情况下&#xff0c;新的容量会是原容量的1.5倍。以JDK1.8为例说明 ArrayList的构造方法有三种&#xff1a; 第一个构造方法用来返回一个初始容量为10的数组 在初…

C++学习资源

https://www.cnblogs.com/xueweihan/p/13928719.html GitHub - Light-City/CPlusPlusThings: C那些事 GitHub - 0voice/introduce_c-cpp_manual: 一个收集C/C新手学习的入门项目&#xff0c;整理收纳开发者开源的小项目、工具、框架、游戏等&#xff0c;视频&#xff0c;书籍&a…

Oracle实现主键字段自增

Oracle实现主键自增有4种方式&#xff1a; Identity Columns新特性自增&#xff08;Oracle版本≥12c&#xff09;创建自增序列&#xff0c;创建表时&#xff0c;给主键字段默认使用自增序列创建自增序列&#xff0c;使用触发器使主键自增创建自增序列&#xff0c;插入语句&…

ceph分布式存储部署

一、概述 是一个统一的分布式存储系统&#xff0c;设计初衷是提供较好的性能、可靠性和可扩展性。 特点 1、统一存储 虽然 ceph 底层是一个分布式文件系统&#xff0c;但由于在上层开发了支持对象和块的接口。所以在开源存储软件中&#xff0c;能够一统江湖。至于能不能千秋万…

【0223】源码剖析smgr底层设计机制(3)

1. smgr设计机制 PG内核中smgr完整磁盘存储介质的管理是通过下面三部分实现的。 1.1 函数指针结构体 f_smgr 函数指针结构体 f_smgr。 通过该函数指针类型,可完成类似于UNIX系统中的VFD功能,上层只需要调用open()、read()、write()等系统函数,用户不必去关系底层的文件系统…

【Vue.js】使用ElementUI搭建动态树数据表格与分页

一&#xff0c;动态树 本文章为上一篇文章拓展内容》》实现首页导航及左侧菜单 将左侧菜单结构更换为下面代码&#xff1a; 菜单结构&#xff1a; <el-menu><el-submenu index"" key""><template slot"title"><i class…