ptmalloc源码分析 - 分配区heap_info结构实现(5)

目录

一、heap_info是什么?

二、heap_info结构图

三、new_heap的实现

四、grow_heap的实现

五、shrink_heap的实现

六、delete_heap的实现


一、heap_info是什么?


上一章节《ptmalloc源码分析 - 多线程争抢竞技场Arena的实现(04)》我们讲解了多线程环境下,面对线程之间的竞争,ptmalloc除了主分配区外,还会去创建非主分配区。因为线程在调用malloc的时候,首先会去获取一个分配区,如果当前的分配区都被锁定并且没有新的分配区可用的时候,ptmalloc就会去创建一个新的非主分配区

我们也从上一章节,知道了非主分配区的几个特性:

  • 每个进程有一个主分配区,也可以允许有多个非主分配区。
  • 主分配区可以使用brk和mmap来分配,而非主分配区只能使用mmap来映射内存块
  • 非主分配区的数量一旦增加,则不会减少。
  • 主分配区和非主分配区形成一个环形链表进行管理。通过malloc_state->next来链接

在函数_int_new_arena中主要是创建一个新的分配区,该分配区主要是非主分配区类型。主分配区在ptmalloc_init中初始化,并且设置了全局变量main_arena的值。而_int_new_arena中主要调用的是new_heap来创建和初始化一个非主分配区。

这里主要包含几个部分:

  • _heap_info:非主分配区的结构体

  • new_heap(size_t size, size_t top_pad):创建一个新的非主分配区

  • grow_heap(heap_info *h, long diff):扩大一个分配区的size

  • shrink_heap(heap_info *h, long diff):缩小一个分配区的size

  • delete_heap(heap):删除一个分配区结构

二、heap_info结构图


 _heap_info来存储分配区的基础信息:

  • ar_ptr:指向当前heap所属的竞技场Arena

  • prev:链表,指向前一个堆的heap_info结构

  • size:当前分配区大小

  • mprotect_size:记录了堆中多大的空间是可读写的

  • pad:通过一个数组的方式,包含该heap_info结构体所需要的内存地址。并且,能够按照0x10字节对齐(x86中则是8字节对齐)

typedef struct _heap_info {mstate ar_ptr; /* 指向当前heap所属的竞技场Arena Arena for this heap. */struct _heap_info *prev; /* 链表,指向前一个堆的heap Previous heap. */size_t size; /* 当前分配区大小 Current size in bytes. */size_t mprotect_size; /* 记录了堆中多大的空间是可读写的 Size in bytes that has been mprotectedPROT_READ|PROT_WRITE.  *//*  Make sure the following data is properly aligned, particularlythat sizeof (heap_info) + 2 * SIZE_SZ is a multiple ofMALLOC_ALIGNMENT. *//* 用以堆其该结构体,使其能够按照0x10字节对齐(x86中则是8字节对齐) */char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;

三、new_heap的实现


new_heap函数说明:创建一个新的堆空间 ,从 mmap 区域映射一块内存页来作为 heap

映射内存页的大小:

  • 在 32 位系统上,该函数每次映射 1M 内存,映射的内存块地址按 1M 对齐;

  • 在 64 为系统上,该函数映射 64M 内存,映射的内存块地址按 64M 对齐。

/* HEAP_MIN_SIZE 和 HEAP_MAX_SIZE 最小和最大值*/
/* HEAP_MAX_SIZE:根据操作系统取值,32位 1M;64位 64M*/
#define HEAP_MIN_SIZE (32 * 1024)
#ifndef HEAP_MAX_SIZE
# ifdef DEFAULT_MMAP_THRESHOLD_MAX
#  define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX)
# else
#  define HEAP_MAX_SIZE (1024 * 1024) /* must be a power of two */
# endif
#endif/* DEFAULT_MMAP_THRESHOLD_MAX : 定义不同操作系统下的值大小 */
# if __WORDSIZE == 32
#  define DEFAULT_MMAP_THRESHOLD_MAX (512 * 1024) //512K
# else
#  define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long)) //32M
# endif
#endif

new_heap函数实现逻辑:

  1. 首先,判断默认设置的size大小,如果size需要在HEAP_MIN_SIZE和HEAP_MAX_SIZE之间(64位系统,HEAP_MIN_SIZE=32k,HEAP_MAX_SIZE=64M)。
  2. 如果size设置过大,则报错;如果过小,则默认使用HEAP_MIN_SIZE作为size的最小值
  3. 创建内存映射区域的时候,使用MMAP函数。每次分配以HEAP_MAX_SIZE为每一页的大小(64位系统 分配一次64M)
  4. 默认情况下,aligned_heap_area值为NULL,所以代码会直接进入p1的分配流程。p1的分配流程中,会一次性分配HEAP_MAX_SIZE*2两个页,如果分配成功,则将aligned_heap_area设置成第2页的起始位置
  5. 当第二次进来分配的时候,aligned_heap_area值不为空,说明前面已经分配过一次,并且aligned_heap_area保存了前一次分配第二页的起始地址,所以这次分配直接使用前一次分配的第二页。如果此次分配失败,则继续跳转到p1重新分配。
  6. 如果p1分配两个页失败,则尝试分配1个页(大小HEAP_MAX_SIZE),如果还是失败则报错
/* 创建一个新的堆空间 从 mmap 区域映射一块内存页来作为 heap* 分配的页大小:* 在 32 位系统上,该函数每次映射 1M 内存,映射的内存块地址按 1M 对齐;* 在 64 为系统上,该函数映射 64M 内存,映射的内存块地址按 64M 对齐。*/
static heap_info *
new_heap(size_t size, size_t top_pad) {size_t pagesize = GLRO(dl_pagesize);char *p1, *p2;unsigned long ul;heap_info *h;/* size 在 HEAP_MIN_SIZE 和 HEAP_MAX_SIZE之间,最小:HEAP_MIN_SIZE,超过HEAP_MAX_SIZE 则返回0 */if (size + top_pad < HEAP_MIN_SIZE)size = HEAP_MIN_SIZE;else if (size + top_pad <= HEAP_MAX_SIZE)size += top_pad;else if (size > HEAP_MAX_SIZE)return 0;elsesize = HEAP_MAX_SIZE;size = ALIGN_UP(size, pagesize);/* A memory region aligned to a multiple of HEAP_MAX_SIZE is needed.No swap space needs to be reserved for the following largemapping (on Linux, this is the case for all non-writable mappingsanyway). *//* aligned_heap_area: 是上一次调用 mmap 分配内存的结束虚拟地址,并已经按照 HEAP_MAX_SIZE 大小对齐。* 如果aligned_heap_area不为空,则从上次虚拟内存地址开始映射HEAP_MAX_SIZE大小的地址 */p2 = MAP_FAILED;if (aligned_heap_area) {p2 = (char *) MMAP(aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,MAP_NORESERVE);aligned_heap_area = NULL;if (p2 != MAP_FAILED && ((unsigned long) p2 & (HEAP_MAX_SIZE - 1))) {__munmap(p2, HEAP_MAX_SIZE);p2 = MAP_FAILED;}}/* 如果第一次分配(aligned_heap_area=NULL) 或者 P2分配失败(aligned_heap_area不为NULL),则开始重新分配* p1表示第一次分配,调用MMAP分配2倍HEAP_MAX_SIZE的内存映射块,本次使用内存块的第一块部分,并将aligned_heap_area指向第二块部分* */if (p2 == MAP_FAILED) {p1 = (char *) MMAP(0, HEAP_MAX_SIZE << 1, PROT_NONE, MAP_NORESERVE); //分配一块两倍HEAP_MAX_SIZE大小的内存映射块,可以分两次使用if (p1 != MAP_FAILED) { //分配成功p2 = (char *) (((unsigned long) p1 + (HEAP_MAX_SIZE - 1))& ~(HEAP_MAX_SIZE - 1));ul = p2 - p1;if (ul)__munmap(p1, ul);elsealigned_heap_area = p2 + HEAP_MAX_SIZE; //aligned_heap_area 记录下一次分配的地址值__munmap(p2 + HEAP_MAX_SIZE, HEAP_MAX_SIZE - ul);} else {/* 如果P1分配2倍的HEAP_MAX_SIZE失败,则再次重试分配HEAP_MAX_SIZE,分配失败则报错*//* Try to take the chance that an allocation of only HEAP_MAX_SIZEis already aligned. */p2 = (char *) MMAP(0, HEAP_MAX_SIZE, PROT_NONE, MAP_NORESERVE);if (p2 == MAP_FAILED)return 0;if ((unsigned long) p2 & (HEAP_MAX_SIZE - 1)) {__munmap(p2, HEAP_MAX_SIZE);return 0;}}}if (__mprotect(p2, size, PROT_READ | PROT_WRITE) != 0) {__munmap(p2, HEAP_MAX_SIZE);return 0;}h = (heap_info *) p2; //写入heap_info结构h->size = size; //设置大小h->mprotect_size = size; //设置大小LIBC_PROBE(memory_heap_new, 2, h, h->size);return h;
}

四、grow_heap的实现


 grow_heap:对堆进行扩容(但是size需要小于分配页的大小HEAP_MAX_SIZE);
实际就是调整size(扩大size,但是size需要小于分配页大小)

/*** 对堆进行扩容,但是size需要小于分配页的大小HEAP_MAX_SIZE* 实际就是调整size(扩大size,但是size需要小于分配页大小)*/
static int grow_heap(heap_info *h, long diff) {size_t pagesize = GLRO(dl_pagesize);long new_size;diff = ALIGN_UP(diff, pagesize);new_size = (long) h->size + diff;if ((unsigned long) new_size > (unsigned long) HEAP_MAX_SIZE)return -1;if ((unsigned long) new_size > h->mprotect_size) {if (__mprotect((char *) h + h->mprotect_size,(unsigned long) new_size - h->mprotect_size,PROT_READ | PROT_WRITE) != 0)return -2;h->mprotect_size = new_size;}h->size = new_size;LIBC_PROBE(memory_heap_more, 2, h, h->size);return 0;
}

五、shrink_heap的实现


 shrink_heap:缩小堆区:实际就是缩小size,通过MMAP函数(参数:MAP_FIXED)

static int shrink_heap(heap_info *h, long diff) {long new_size;new_size = (long) h->size - diff;if (new_size < (long) sizeof(*h))return -1;/* Try to re-map the extra heap space freshly to save memory, and make itinaccessible.  See malloc-sysdep.h to know when this is true.  */if (__glibc_unlikely(check_may_shrink_heap())) {if ((char *) MMAP((char *) h + new_size, diff, PROT_NONE, MAP_FIXED)  //丢弃操作== (char *) MAP_FAILED)return -2;h->mprotect_size = new_size;} else__madvise((char *) h + new_size, diff, MADV_DONTNEED);/*fprintf(stderr, "shrink %p %08lx\n", h, new_size);*/h->size = new_size;LIBC_PROBE(memory_heap_less, 2, h, h->size);return 0;
}

六、delete_heap的实现

/* Delete a heap. */#define delete_heap(heap) \do {									      \if ((char *) (heap) + HEAP_MAX_SIZE == aligned_heap_area)		      \aligned_heap_area = NULL;					      \__munmap ((char *) (heap), HEAP_MAX_SIZE);			      \} while (0)

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

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

相关文章

ATA-2161高压放大器的电子实验案例(案例合集)

ATA-2161是一款理想的可放大交直流信号的单通道高压放大器。最大差分输出1600Vp-p(800Vp)高压&#xff0c;可以驱动高压型负载。凭借其优异的指标参数受到不少电子工程师的喜欢&#xff0c;其在电子实验中的应用也非常频繁&#xff0c;下面为大家整理出ATA-2161高压放大器的应用…

代码随想录打卡—day46—【DP】— 8.29 背包END

1 139. 单词拆分 139. 单词拆分 做了很久...估计2h 一开始我的思路卡死了 看题解之后的思路的详解见注释&#xff0c; 我的写法和carl 答案在一些微小的细节上略有不同&#xff0c;我的更好理解&#xff0c;但他的解法更简单。 我写的过程中&#xff0c;需要注意下标和字符…

为何直线导轨要保持日常清洁?

随着时代的发展&#xff0c;已逐步从传统的工业发展模式发展到工业自动化&#xff0c;直线滑轨在工业生产中得到了广泛的应用&#xff0c;大大提高了生产效率&#xff0c;带动了经济效益的增长。 众所周知&#xff0c;想要直线导轨的使用达到预期的效果&#xff0c;日常的保养和…

【论文笔记】Planning and Decision-Making for Autonomous Vehicles

文章目录 Summary1. INTRODUCTION2. MOTION PLANNING AND CONTROL2.1. Vehicle Dynamics and Control2.2. Parallel Autonomy2.3. Motion Planning for Autonomous Vehicles 3. INTEGRATED PERCEPTION AND PLANNING3.1. From Classical Perception to Current Challenges in Ne…

1688API技术解析,实现获得1688商品详情

要实现获得1688商品详情&#xff0c;你需要使用1688 API。1688 API是阿里巴巴旗下的开放平台&#xff0c;它提供了一套丰富的接口&#xff0c;可以让开发者通过编程的方式获取到1688网站上的商品信息。 首先&#xff0c;你需要在阿里开放平台注册一个账号&#xff0c;并创建一…

Celery task 执行报错 TypeError: Object of type set is not JSON serializable 问题分析处理

情况描述&#xff1a; 定义了新的shared_task推送到生产环境后&#xff0c;发现无法执行&#xff0c;会报错set对象无法序列化&#xff0c;报错内容如下&#xff1a; Traceback (most recent call last):File "/tmp/venv/lib64/python3.6/site-packages/kombu/serializati…

6 Python的异常处理

概述 在上一节&#xff0c;我们介绍了Python的面向对象编程&#xff0c;包括&#xff1a;类的定义、类的使用、类变量、实例变量、实例方法、类方法、静态方法、类的运算符重载、继承等内容。在这一节中&#xff0c;我们将介绍Python的异常处理。异常是指程序在运行过程中出现的…

C++中数组作为参数进行传递方法

文章目录 基础&#xff1a;数组作为函数形参示例&#xff1a;1、一维数组的传递&#xff08;1&#xff09;直接传递&#xff08;2&#xff09;指针传递&#xff08;3&#xff09;引用传递 2、二维数组的传递&#xff08;1&#xff09;直接传递&#xff08;2&#xff09;指针传递…

Java中word转Pdf工具类

背景&#xff1a; 最近做的一个项目中&#xff0c;对于word转Pdf用的地方很多&#xff0c;特此记录 搭建总图&#xff1a; 代码部分&#xff1a; 1.需要的jar包&#xff1a; aspose-words-15.8.0-jdk16.jar 注&#xff1a;下载好这个jar包后&#xff0c;在项目的根目录新建一…

第一个react应用程序并添加样式

编写第一个react应用程序 将目录下的文件、src文件夹、public文件夹清空&#xff0c;项目根目录下新建一个文件index.js 在文件中写入以下代码 import React from react import ReactDOM from react-dom ReactDOM.render(<h1>欢迎进入React的世界</h1>,document.…

请使用 -source 7 或更高版本以启用 diamond 运算符

1、遇到问题 Maven打包 C:\Users\chengyq\eclipse-workspace\webservice>mvn package 复制 需要如下错误 [ERROR] COMPILATION ERROR : [INFO] ------------------------------------------------------------- [ERROR] /D:/Users/chengyq/eclipse-workspace/webservic…

利用fsimage分析HDFS小文件

一、Hive 小文件概述 在Hive中&#xff0c;所谓的小文件是指文件大小远小于HDFS块大小的文件&#xff0c;通常小于128 MB&#xff0c;甚至更少。这些小文件可能是Hive表的一部分&#xff0c;每个小文件都包含一个或几个表的记录&#xff0c;它们以文本格式存储。 Hive通常用于…

clickhouse(十四、分布式DDL阻塞及同步阻塞问题)

文章目录 一、分布式ddl 阻塞、超时现象验证方法解决方案 二、副本同步阻塞现象验证解决方案 一、分布式ddl 阻塞、超时 现象 在clickhouse 集群的操作中&#xff0c;如果同时执行一些重量级变更语句&#xff0c;往往会引起阻塞。 一般是由于节点堆积过多耗时的ddl。然后抛出…

云计算和Docker分别适用场景

在大规模网络爬虫系统中&#xff0c;通过使用云计算和Docker技术&#xff0c;可以实现大规模网络爬虫系统的高效架构设计和部署。这种架构能够提供可扩展性、高可用性和灵活性&#xff0c;为爬虫系统的运行和管理带来便利。 云计算和Docker在大规模网络爬虫系统中有不同的业务…

nodejs替换模版中${}的内容

要在js中想要替换替换模板中的${}&#xff0c;可以使用字符串的replace()方法结合正则表达式或者函数来实现替换操作。 以下是两种常见的替换方式&#xff1a; 使用正则表达式&#xff1a; 方法一&#xff1a; const template "Hello, ${name}! Today is ${day}."…

《游戏编程模式》学习笔记(九)游戏循环 Sequencing Patterns

定义 一个游戏循环会在游玩时不断运行。 每一次循环&#xff0c;它都会无阻塞地处理玩家的输入&#xff0c;更新游戏的状态&#xff0c;渲染游戏。它追踪时间的消耗并控制游戏的速度。游戏循环需要做到始终以固定的速度运行游戏。 一个游戏循环中通常包含处理输入部分&#xf…

CTFhub-文件上传-无验证

怎样判断一个网站是 php asp jsp 网站 首先&#xff0c;上传用哥斯拉生成 .php 文件 然后&#xff0c;用蚁剑测试连接 找到 flag_1043521020.php 文件&#xff0c;进去&#xff0c;即可发现 flag ctfhub{ee09842c786c113fb76c5542}

【校招VIP】算法考点之堆排

考点介绍&#xff1a; 排序算法属于数据结构和算法的基础内容&#xff0c;并且也是大厂笔试中的高频考点。 堆排序是使用一棵树存储序列这个课树只保证跟节点是这棵树中的最小值&#xff0c;但并不保证其他节点是按顺序的。因此他的排序是每次从堆中取得堆顶&#xff0c;取得 n…

leetcode 563.二叉树的坡度

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;https://leetcode.cn/problems/binary-tree-tilt/description/ 代码&#xff1a; class Solution { public:int childFind(TreeNode* root , int& sumTile) {if (root nullptr) {return 0; // 空树坡度为0}int l…

Qt中的垂直布局QVBoxLayout和水平布局QHBoxLayout

文章目录 QVBoxLayoutQHBoxLayout QVBoxLayout Qt中的垂直布局&#xff08;Vertical Layout&#xff09;是用来将控件按垂直方向进行排列的布局管理器。下面是一些常用的Qt Vertical Layout的函数及其用法示例&#xff1a; QVBoxLayout类的构造函数&#xff1a; QVBoxLayout…