前言
呵呵 在 c 语言中 malloc 应该是初学者必须了解的一个函数了吧
但凡 涉及到堆内存分配的相关, 必定会使用到 malloc, realloc, calloc 这几个函数
其中 malloc 最常见, 也是最 实用
在 HotspotVM 中也经常会看到 malloc 的身影
我们这里 来调试一下 malloc 的相关的一些场景
本文主要的主题有四个
1. malloc(20) 申请内存, 然后 brk 传递的参数是 132K
2. malloc 第一次内存分配 和 第二次内存分配
3. malloc 分配的内存的 header
4. malloc 20, 但实际可用的空间只有 16
测试用例
测试用例如下, 很简单的一个 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);
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
x + y = 5
1. malloc(20) 申请内存, 然后 brk 传递的参数是 132K
如上测试用例, malloc 20 申请内存的时候, 会使用系统调用 brk 来进行虚拟内存分配
但是 奇怪的是分配的内存是 132k, 我命名只需要 20字节, 为什么 malloc 调用 brk 却申请了 132k 的虚拟内存, 这是为什么呢?
如下图, 查看 brk 系统调用, 申请的 135168 字节, 合计 132k 的虚拟内存
接下来我们看一下 malloc 是怎么做的? 这个 132k 又是怎么来的呢?
大致看一下 上下文, 是在 main 中的第一个 malloc(20) 的地方调用了 brk 系统调用
看一下 increment, 果然是 135168
然后我们再来看一下 这个 135168 是怎么计算出来的
这里有两个过程, 计算加对齐, 计算阶段为 32 + 131072 + 32 = 131136
然后和 4096 对齐一下, 之后的结果为 135168
然后另外一个问题是, 用户程序 申请的是 20 字节, 为什么实际申请的是 32 字节呢
如下 checked_request2size 就是做的这个转换
checked_request2size 的计算方式如下
代入 req 为 20, (20 + 8 + 15) & (0xfff0) = 32
整个表达式的意思是找到比 (20 + 8) 大的最小的 16 的整数倍
注意这里的 8, 额外只是加了一个 SIZE_SZ
2. malloc 第一次内存分配 和 第二次内存分配
malloc 的分配流程为 fastbin, small_bin, unsorted_chunks, 迭代 bins, top
在 Test01Sum 进入第一个 malloc 的时候均无可用空间, 直接到 use_top
此时 av->top 无可用空间, 调用 sysmalloc 申请空间
sysmalloc 中调用 brk 申请 所需要的 135168 字节的空间
然后之后是 申请需要的 32 字节 空间
p / av->top 初始的值为 0x602000, 分配了 32 字节之后 remainder / av-> top 为 0x602020
分别更新申请的 chunk 块的 size, 和 remainder 的 size
然后 chunk2mem 的转换, 增加了 2 * SIZE_SZ = 16, 值为 0x602010
分配的 chunk 的地址 + 两个头的大小[prev_size, size] 即为响应给用户的地址
第二次 malloc 的内存分配, 同上面 其他 fast_bin, small_bin 均无可用空间, 到 use_top
第一次内存分配之后, av->top 为 0x602020, 用户需要空间为 32 字节
因此 切割一个 32 byte 的 chunk 块 p 之后, av->top 为 0x602040
分别更新申请的 chunk 块的 size, 和 remainder 的 size
p 的值为 0x602020, 然后 chunk2mem 的转换, 增加了 2 * SIZE_SZ = 16, 值为 0x602030
至于第三次内存分配, 就和 第二次差不多了, 然后 我们再来回顾一下 程序的输出
p1 : 0x602010
p2 : 0x602030
p3 : 0x602050
x + y = 5
3. malloc 分配的内存的 header
看一下我们的 malloc_chunk, 头部有 2 * 8 个字节, 分别的逻辑意义为 prev_size, size
在上面的流程中我们没有看到 prev_size 的使用, 呵呵 这个我们后续再来记录
如果 前一个 chunk 空闲, 则 prev_size 记录的事该 chunk 的空间大小, 如果前一个 chunk 在使用, 则 prev_size 可以被前一个 chunk 使用
size 记录的数据如下
其中 PREV_INUSE 为 1, NON_MAIN_AREA 为 4, 这里可以知道最后 1bit 标记的是 PREV_INUSE, 倒数第三bit 标记的是 NON_MAIN_ARENA
倒数第二 bit 标记的是, 内存块是使用的 brk 还是 mmap 来分配的空间
然后 mask 掉后面 3bit, 的大小即为当前 chunk 的空间大小
第一次 malloc 分配的 chunk, chunk 的起始地址为 0x602000, 给用户的起始地址为 0x602010
0x602000 起始的的 long 值为 0, 默认第一个 chunk 的前一个 chunk 在使用, prev_size 没有作用
0x602008 起始的的 long 值为 chunk 的 size, 为 0x21, 表示了当前 chunk 空间是 32byte, MAIN_ARENA, 系统调用 brk 分配的内存
第二次 malloc 分配的 chunk, chunk 的起始地址为 0x602020, 给用户的起始地址为 0x602030
0x602020 起始的的 long 值为 0, 前一个 chunk 在使用, prev_size 没有作用
0x602028 起始的的 long 值为 chunk 的 size, 为 0x21, 表示了当前 chunk 空间是 32byte, MAIN_ARENA, 系统调用 brk 分配的内存
当然 这里也可以看到 上面 第一个 chunk 的情况
4. malloc 20, 但实际可用的空间只有 16
如果 您够仔细的话, 你会发现 用户申请的是 20 字节
然后 malloc 头, 占用的空间为 16 字节, 那么 至少应该申请 48 字节才对
但是 为什么只申请了 32 字节?
去掉头的 16 字节, 只有 16 字节了?? 这是 怎么回事
如果 前一个 chunk 空闲, 则 prev_size 记录的事该 chunk 的空间大小, 如果前一个 chunk 在使用, 则 prev_size 可以被前一个 chunk 使用
这个需要理解 prev_size 的使用了, 按照上面的反向推导一下
如果当前 chunk 正在使用, 那么 他是可以使用下一个 chunk 的 prev_size 的
因此 可以回顾一下上面这一段, 根据 用户申请的字节数, 计算需要申请的字节数 这一段, 计算的时候 只是额外增加了一个 SIZE_SZ, 因为当前 chunk 正在使用, 可以额外使用下一个 chunk 的 prev_size, 因此 这里的理解 和 上面的 checked_request2size
注意这里的 8, 额外只是加了一个 SIZE_SZ
5. malloc 0, 会怎么处理?
如果 bytes 为 0, 在 checked_request2size 的时候, malloc 会有最小分配空间的需求, 按照 MINSIZE 来进行分配空间, MINSIZE 为 32 字节
所以 得到的还是一块 32 字节的空间, 但是 至于你如何使用 并没有具体强制约束
checked_request2size 如下, 调整了 sz, 如果 小于 MINSIZE 设置 sz 为 MINSIZE
文章来源:https://www.toymoban.com/news/detail-405548.html
完 文章来源地址https://www.toymoban.com/news/detail-405548.html
到了这里,关于16 malloc 虚拟内存分配的调试(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!