17 malloc 虚拟内存分配的调试(2)

前言

接着文章 malloc 虚拟内存分配的调试(1)

呵呵 在 c 语言中 malloc 应该是初学者必须了解的一个函数了吧 

但凡 涉及到堆内存分配的相关, 必定会使用到 malloc, realloc, calloc 这几个函数 

其中 malloc 最常见, 也是最 实用 

在 HotspotVM 中也经常会看到 malloc 的身影 

我们这里 来调试一下 malloc 的相关的一些场景 

本文主要的主题有四个
 

1. free 的处理
2. fastbin, smallbin 的内存分配
3. brk/mmap 分配的系统内存
4. 多次 free 同一块空间 
5. 修改 malloc 的 header, 然后 free 会发生什么?

测试用例

测试用例如下, 很简单的一个 case, 主要的目的在于使用 malloc, 以及观察 malloc 分配的内存的虚拟地址信息 

root@ubuntu:~/ClionWorkStations/HelloWorld# cat Test01Sum.c 
#include "stdio.h"

int main(int argc, char** argv) {

int x = 2;
int y = 3;
int z = x + y;

void *p1 = malloc(20);
void *p2 = malloc(20);
void *p3 = malloc(20);
printf("p1 : 0x%x\n", p1);
printf("p2 : 0x%x\n", p2);
printf("p3 : 0x%x\n", p3);

free(p2);
void *p4 = malloc(20);
printf("p4 : 0x%x\n", p4);

printf(" x + y = %d\n ", z);

}

编译使用我们自己手动编译的 glibc 库

export mainClass=Test01Sum
 
gcc $mainClass.c -o $mainClass -L /root/Desktop/linux/glibc-2.23/install/lib -Wl,--rpath=/root/Desktop/linux/glibc-2.23/install/lib -Wl,-I /root/Desktop/linux/glibc-2.23/install/lib/ld-linux-x86-64.so.2

程序输出结果如下 

p1 : 0x602010
p2 : 0x602030
p3 : 0x602050
p4 : 0x602030
 x + y = 5

1. free 的处理

在这里场景中的 free 的调用 

free 掉的 chunk 空间为 32bytes, 属于 fastbins, 这里将该 chunk 会收到 av->fastbinsY 中 

这里的 av->fastbinsY[0], 对应于 32 bytes 的 bin 的链表, 因此将 p2 0x602020 会收到了 av->fastbinsY[0] 中维护 

下一次 malloc 的时候 fastbinsY 链表中就可用的空间去分配了 


2. fastbin, smallbin 的内存分配 

这是第四个 malloc 的地方, free 释放了一个 32 bytes 的 chunk, 将空间归还给了 av->fastbinsY 中 

然后 这里 malloc 的时候, 需要分配 32bytes, 查询 av->fastbinsY[0], 找到可用的空间 cas 更新 av->fastbinsY[0], 获取到可用的空间, 然后 将空间 返回回去 

这里响应回去的空间为 free 掉的 p2 的空间, chun 的地址为 0x602020, 返回给用户的地址为 0x602030 

为了测试 smallbin, 我们将分配的内存更新为 200 字节  

p1, p2, p3 的分配均是从 brk 分配出来的内存中直接分配, 这不是我们关心的部分, free(p2) 会将 p2 对应的 chunk 归还回 av, p4 的空间分配来自于 归还之后的 smallbin 

顺序上来看, 我们先看 free(p2) 

如果 p2 前面一个 chunk 未使用, 连接前一个 chunk 和当前 chunk, 如果下一个 chunk 未使用, 连接当前 chunk 和 下一个chunk, 合并成一个 pendingChunk 

将 pendingChunk 注册到 av->unsorted_chunks 中, 这里 av->unsorted_chunks 为初始化的一个 chunk, 所以添加了 pendingChunk 之后, av->unsorted_chunks 期望有两个元素 

从 bck / fwd 开始往下路由, 是可以看到刚才回收的 p2 对应的 chunk 

接下来的 p4 的内存分配 

上面 p2 的空间是回收给了 av->unsorted_chunks, 这里 p4 的空间来自于 av->unsorted_chunks, 遍历 av->unsorted_chunks 列表, 找到了 p2 的回收的空间 满足 这里 p4 的分配需求 

将 p2 回收的空间从 av->unsorted_chunks 中摘除, 并更新 下一个 chunk 的 PREV_INUSE, 返回 空间给用户 

  

如果是 smallbin 中进行内存分配 

根据 需求的大小, 计算 bin 的索引, 然后 从 bin[idx] 中提取给定的空间, 更新 bin[idx] 链表 

更新 下一个 chunk 的 PREV_INUSE, 返回 空间给用户 

3. brk/mmap 分配的系统内存

为了测试 mmap 系统调用分配虚拟内存, 我们将分配的内存更新为 200 字节  

执行结果如下 

p1 : 0xf7fc3010
p2 : 0xf7f92010
p3 : 0xf7f61010
p4 : 0x602420
 x + y = 5

假设需求的空间大于 map_mmap_threshold [128kb], 使用 mmap 系统调用来进行空间分配 

更新 chunk 对应的 size, 并增加 IS_MAPPED 的标记 

更新 mp_ 的 mmap 相关的元数据 

另外一个问题就是 p4 拿到的地址 似乎是和前面三个 mmap 拿到的地址不一样, 这是怎么回事呢?

这是因为 mp_.mmap_threshold 的阈值已经被调整了, 具体的调整的地方 是在 free 的地方

 这里可以动态的调整 mmap_threshold, 只要曾经释放过 比 mmap_threshold 大的 mmap 的空间, 更新 mmap_threshold 为更大是放过的这个更大的空间 

4. 多次 free 同一块空间 

fastbins 中的 double free 的校验, 然后 后面触发了 异常处理相关 

smallbin 的 double free 的校验, 然后 后面触发了 异常处理相关 

mmap 的 double free 的校验, 主要是来自于 munmap 之后对于空间的使用 触发的异常 

  

5. 修改 malloc 的 header, 然后 free 会发生什么?

我们这里 只演示一种情况 

调整部分代码片段如下 

void *p1 = malloc(20);
void *p2 = malloc(20);
void *p3 = malloc(20);
printf("p1 : 0x%x\n", p1);
printf("p2 : 0x%x\n", p2);
printf("p3 : 0x%x\n", p3);

*((char*)0x602028) = 65;
free(p2);

可以看到这里 free 的时候 计算 fastbinY 的索引已经计算错了 

实际这个 chunk 的空间只分配了 32 bytes, 应该是属于 fastbinsY[0] 

但是这里强行将 header 中的 size 更新成了 64 bytes, 导致这里计算索引 更新成了 fastbinsY[2] 

会导致一些 意料之外的异常 

猜你喜欢

转载自blog.csdn.net/u011039332/article/details/127591557