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適配,池緩存,雙鏈表緩存。