ptmalloc - 小块内存管理初探
停更了两个月,一方面是转去了django开发,需要时间去学习,另一方面,好游戏比较多。

不过放心,现在开始专心开车,带你游历glibc ptmalloc簇函数实现。
前面已经写过几篇文章了,算是初探,也没有特别的深入到内存块管理方式,不过,基本的感性认知已经是有了,例如知道用到了sbrk,mmap等。

要开车了,坐稳吧。
要点
- Ptmalloc里面的内存管理
- 预备知识
- 小块内存初探
- 结尾
Ptmalloc里面的内存管理
是不是好奇为啥没有了用例,这一章是纯代码解释,不好放用例,放了用例开展更多的代码,车速不是你们能够承受的。
按照glibc/malloc/malloc.c里面的注释,写了四种内存管理算法,我大概说下
- 对于大内存(大于512byte)的申请,就是用 最佳适用申请器 加上FIFO
- 小块的(小于等于64byte),就是有个池缓存着块
- 介于中间的,希望结合大小块管理方式
- 大于等于128KB的,就是之前那篇《第一次申请与释放大内存》
我大概翻译了下,不完全准确,也别纠结,个人觉得给你原文你也不一定能知道究竟具体是什么。拉上安全带吧,上代码了。今天就初探下第三点。
预备知识
第一点, struct malloc_chunk 相关的结构,在《第一次申请小内存》里面说过了,不记得么,想我重新说么

自己看去
第二点,起码要知道什么是链表,不懂我教你啊,学费一小时1k,刚好缺钱买键盘
第三点,应该没有了,不过要凑个三,处女座嘛
小块内存初探
代码
代码不多,先全部贴上来,老规矩,跟主题无关的代码就直接砍了
static void *
_int_malloc (mstate av, size_t bytes)
{
....
// [0]
if (in_smallbin_range (nb)) {
idx = smallbin_index (nb);
bin = bin_at (av, idx);
// [1]
if ((victim = last (bin)) != bin) {
// [2]
if (victim == 0) {
malloc_consolidate (av);
// [3]
} else {
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;
}
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
}
....
[0]
三句代码,就是三个宏
#define MIN_LARGE_SIZE 1024
#define in_smallbin_range(sz) \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
#define smallbin_index(sz) ((sz) >> 4)
#define bin_at(m, i) \
(mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) - \
offsetof (struct malloc_chunk, fd))
为何 MIN_LARGE_SIZE 是1024?512是32位,1024是64位。
这里重点解释一下smallbin_index
sz >> 4 其实就是等于 sz / 16,而接下来是用smallbin_index算出的idx去索引bin,sz是长度,是申请的内存块的长度,那这里其实的意思就是,bin是一个结构体数组,数组里面每个元素代表着一种长度,长度的间隔是16。
那bin具体怎样的呢,往下看。
#define NBINS 128
struct malloc_state
{
....
mchunkptr top;
mchunkptr last_remainder;
mchunkptr bins[NBINS * 2 - 2];
....
}
typedef struct malloc_chunk* mchunkptr;
struct malloc_chunk {
size_t mchunk_prev_size; /* Size of previous chunk (if free). */
size_t mchunk_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;
};
bin_at展示的是一种典型的c中指针应用法,但是就不展开说了,车控制不住。
回头看看malloc_chunk里面的fd和bk,再看看注释,是不是很明显了,双链,每个释放掉的内存块就会根据大小链在对应的bin中,而且,每个内存块都保证是16的倍数。
[1]
#define last(b) (b)->bk
这一句的意思就是说,看看这个是不是空的,没有内存块的,初始化时,其实就是bk,fd都指向自己。
[2]
malloc_consolidate调用的是malloc_init_state
static void
malloc_init_state (mstate av)
{
int i;
mbinptr bin;
/* Establish circular links for normal bins */
for (i = 1; i < NBINS; ++i) {
bin = bin_at (av, i);
bin->fd = bin->bk = bin;
}
#if MORECORE_CONTIGUOUS
if (av != &main_arena)
#endif
set_noncontiguous (av);
if (av == &main_arena)
set_max_fast (DEFAULT_MXFAST);
av->flags |= FASTCHUNKS_BIT;
av->top = initial_top (av);
}
bin->fd = bin->bk = bin; 这一句就解释了 if ((victim = last (bin)) != bin) 这一句的来源了。其他代码就是设置标记。
[3]
把内存块拿下来,补链表,返回内存块。
结尾
虽然,没有看到bin里面的内存块是怎么放上去的,不过,起码知道,是根据内存大小分区存储,每个区是16字节差距,另外也知道是用双链表。这里已经符合了开头注释,最佳适配,用idx适配,池缓存,双链表缓存。
本文探讨了ptmalloc中小块内存(≤64字节)的管理方式,通过使用双链表缓存和最佳适配算法,实现了内存的有效分配与回收。
5831

被折叠的 条评论
为什么被折叠?



