ptmalloc - 小塊內存管理初探

ptmalloc - 小塊內存管理初探


停更了兩個月,一方面是轉去了django開發,需要時間去學習,另一方面,好遊戲比較多。

不過放心,現在開始專心開車,帶你遊歷glibc ptmalloc簇函數實現。

前面已經寫過幾篇文章了,算是初探,也沒有特別的深入到內存塊管理方式,不過,基本的感性認知已經是有了,例如知道用到了sbrk,mmap等。

要開車了,坐穩吧。

要點


  • Ptmalloc裏面的內存管理
  • 預備知識
  • 小塊內存初探
  • 結尾

Ptmalloc裏面的內存管理


是不是好奇爲啥沒有了用例,這一章是純代碼解釋,不好放用例,放了用例開展更多的代碼,車速不是你們能夠承受的。

按照glibc/malloc/malloc.c裏面的註釋,寫了四種內存管理算法,我大概說下

  1. 對於大內存(大於512byte)的申請,就是用 最佳適用申請器 加上FIFO
  2. 小塊的(小於等於64byte),就是有個池緩存着塊
  3. 介於中間的,希望結合大小塊管理方式
  4. 大於等於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適配,池緩存,雙鏈表緩存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章