前言

这里整理一下堆有关的学习笔记,因为我在学习过程中经常苦于网上的讲解大多都直接放源码,让我苦于读枯燥的代码,所以我想把这些知识点用我自己的话表达出来(当然,为了严谨也会贴些源码),用以促进自己的理解,如果能帮到其他人那就更好了。

堆的初始化

首先来讲解一下堆的初始化和一些基础知识。

堆并不是程序运行时就存在的,而是在程序第一次进行堆块申请的时候(也可以先视为第一次调用malloc)才开始堆的初始化。

堆初始化时,会调用sbrk()申请一块内存,在这块内存用完前,你申请的堆块都会从这块内存中分割。随后会从这块内存中分出你malloc的堆块给你,剩下的就是top chunk。之后在你再次malloc的时候,如果没有空闲的堆块或者空闲的堆块大小不够的话,就会从top chunk的低地址割下来一块给你。因此,你获得的堆块都是物理相邻的。

堆块的数据结构

这里先贴一段源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {

INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

好的看不懂没关系,我会详细解释的。
首先,堆块必须按2倍机器字长对齐,也就是说32位程序必须8字节对齐,64位必须16字节对齐。

0x0

每个堆块0x0的位置(占8个字节)是堆块的prev_size字段,如果与该堆块物理相邻的前一个堆块处于空闲状态,那这个地方用于存前一个堆块的大小,若前一个堆块处于使用中则这个地方用处不大,什么都不存。

0x8

在0x8的位置(占8个字节)是堆块的size字段,存放该堆块的大小,不过由于堆块都是16字节对齐的,也就是说,都是0x10的倍数,诶你会发现最后一个数永远是0,没有实际意义,所以,这一位数被额外地利用了起来,用来表示该堆块是否在使用中(依旧是inuse位),使用中就是1,空闲就是0(被free了的堆块就是空闲的堆块)。

由于每个堆块(chunk),都必须得有这两个共占16字节的字段,再加上chunk要求16字节对齐,所以chunk最小为32字节,哪怕你malloc申请的数字小于这个值,返回的chunk仍然会是32字节的(这里要注意,malloc函数的参数实际上是指一个堆块去除开头这16字节后的大小,也就是实际可以用的内存大小)。

0x10

从0x10这里开始,就是可正常使用的内存了,实际上malloc返回的指针就是指向这里。

不过这里要注意,很多初学者都会把指向堆块的指针和堆块搞混,需要弄明白的是,堆块并不会因为指向他的指针的改变而改变。

但是如果堆块被free了,处于空闲状态的话,就会根据堆块大小放进不同的bin中,而bin都是用链表来串联管理堆块的,所以0x10和0x18这两个位置就会放两个堆指针,fd和bk,分别指向bin链表中,该堆块的前一个和后一个堆块(注意不是物理相邻的前后)。

所以堆块说白了就是个内存块,你无论做什么操作,祂的地址都不会变,祂就在那里,无论是什么空闲状态还是使用状态,在哪个bin中本质上都是在堆块中写数据达成的,只不过有的数据仅仅只起到标识作用(inuse位),有的还有其他功能。

0x20

而最后,0x20这里,在绝大多数情况下都只是用来存数据,只有这个堆块被放进largebin时,这里会放两个指针fd_nextsize和bk_nextsize,这两个指针也是用来串联链表的,具体的功能我放在后面详细讲largebin的时候再细说。

后记

这里放张示意图来辅助理解。

图片
这篇都是简单的知识点,本来还想再多写点,结果发现不管再写什么,想写清楚都少不了一番功夫,所以索性这篇就只写这么短了。