ptmalloc - malloc 终章

ptmalloc - malloc 终章


是不是觉得我的文章名起得很不好,我也觉得,不过,反正我是程序员,只要变量名、函数名、类名和文件名起得好就行了,其他的,不管。

不过,实话实说,这是关于malloc函数的终章了,malloc其实就是包含了几个重要函数,__libc_malloc -> _int_malloc -> sysmalloc,__libc_malloc和sysmalloc已经全部说过了,_int_malloc也提到部分。这一章讲解的就是_int_malloc里面最后的一部分,也是最长的一部分,是不是感觉看这么多代码很累,是的,反正我复制粘贴我都觉得累。

目录


  • _int_malloc的结构
  • unknown
  • 总结

_int_malloc的结构


_int_malloc 里面的代码按照功能,其实可以分割为以下部分

  • 从fast中获取内存块 (ptmalloc - 小小内存的分配和申请)
  • 从regular bin (medium size) 中获取内存块 (ptmalloc - 小块内存管理初探)
  • code = unknown
  • 所有地方都匹配不到适合的,就sysmalloc (ptmalloc - 第一次申请小内存)

unknown就是我们这章要讨论的

诚实预告片,接近要看300+行的代码,没耐心请点关闭。

unknown


先看一下最外层的代码吧

static void *
_int_malloc(mstate av, size_t bytes)
{
   /* ... */

   /* unknown start */
   for (; ; ) {
       /* ... */
   }
}

别激动!正戏开始了。

unknown part1 - unsort


在释放一块内存的时候,free是不会内存块放进去regular bin里面的,而是会把内存块进入一个叫unsort的双链表里面,关于free的详解,以后会说到,现在先说说unsort。

        /* ... */

        /* 如果unsort里面的back指针不等于自身,说明有未排序的内存块在 */
        while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) {
            bck = victim->bk;
            size = chunksize (victim);

           /* 如果申请的内存是小内存 */
            if (in_smallbin_range (nb) &&
                    /* 并且unsort里面也只剩下一块内存了 */
                    bck == unsorted_chunks (av) &&
                    /* 当前块在分配小内存时被分割过,具体设置下面有 */
                    victim == av->last_remainder &&
                    /* 从sort里面拿出来的那块内存大于申请的 */
                    (unsigned long) (size) > (unsigned long) (nb + MINSIZE)) {

                /* 将unsort中获取的那块分割成两半 */
                remainder_size = size - nb;
                remainder = chunk_at_offset (victim, nb);

                /* 将分隔剩下的那块取代就有的那块链回去unsort */
                unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
                av->last_remainder = remainder;
                remainder->bk = remainder->fd = unsorted_chunks (av);

                /* 与大块内存相关的指针设置 */
                if (!in_smallbin_range (remainder_size)) {
                    remainder->fd_nextsize = NULL;
                    remainder->bk_nextsize = NULL;
                }

                /* 设置获取到的内存块的头部参数 */
                set_head (victim, nb | PREV_INUSE |
                        (av != &main_arena ? NON_MAIN_ARENA : 0));

                /* 设置分割剩下的内存块的头部参数 */
                set_head (remainder, remainder_size | PREV_INUSE);
                set_foot (remainder, remainder_size);

                return chunk2mem (victim);
            }

            /* ... */
        }

        /* ... */

能碰到unsort里面只剩下一块内存的情况就是,要么是只放了一块,要么是跑了那么多个循环没碰到适合的,也只是剩下一块内存了,这两种情况下,好好的使用这块内存,分割下,另外的存起来也是无可厚非,你也可以当成一种垂死挣扎,快点脱离循环的一种渴望。

            /* 把victim从unsort中除名 */
            unsorted_chunks (av)->bk = bck;
            bck->fd = unsorted_chunks (av);

            /* 如果刚刚好victim的size等于申请的 */
            if (size == nb) {
                set_inuse_bit_at_offset (victim, size);
                return chunk2mem (victim);
            }

上上面的看看是否能够分割剩下的那块内存是垂死挣扎,那么到了这里才是步入正规

            if (in_smallbin_range (size)) {
                /* 小块内存就好办了,直接链上对应的bin */
                victim_index = smallbin_index (size);
                bck = bin_at (av, victim_index);
                fwd = bck->fd;

            } else {
                victim_index = largebin_index (size);
                bck = bin_at (av, victim_index);
                fwd = bck->fd;

                /* 当前链表是否为空 */
                if (fwd != bck) {
                    size |= PREV_INUSE;

                    /* large bin的是按照大小排序的,恰好,bck->bk就是size最小那一块 */
                    if ((unsigned long) (size)
                            < (unsigned long) chunksize_nomask (bck->bk)) {

                        fwd = bck;
                        bck = bck->bk;

                        /* 链上size链 */
                        victim->fd_nextsize = fwd->fd;
                        victim->bk_nextsize = fwd->fd->bk_nextsize;
                        fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;

                    } else {
                        /* 找最小 */
                        while ((unsigned long) size < chunksize_nomask (fwd)) {
                            fwd = fwd->fd_nextsize;
                        }

                        /* 
                            如果恰好相等,那我就不放上size链了,从当前代码来看
                            size只是大小的排序,有一个就好了
                        */
                        if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd)) {
                            /* Always insert in the second position.  */
                            fwd = fwd->fd;

                        } else {
                            victim->fd_nextsize = fwd;
                            victim->bk_nextsize = fwd->bk_nextsize;
                            fwd->bk_nextsize = victim;
                            victim->bk_nextsize->fd_nextsize = victim;
                        }

                        bck = fwd->bk;
                    }

                } else {
                    /* 初始化当前large bin的size双链 */
                    victim->fd_nextsize = victim->bk_nextsize = victim;
                }
            }

            /* 标记当前bin已使用,具体下面章节会说到 */
            mark_bin (av, victim_index);

            /* 链上链表 */
            victim->bk = bck;
            victim->fd = fwd;
            fwd->bk = victim;
            bck->fd = victim;

#define MAX_ITERS   10000
            /* 如果处理了10000块unsort还是没有心仪的,那就走吧,不挽留了 */
            if (++iters >= MAX_ITERS)
                break;
        }

看了上面的代码,是不是觉得,好像还是挺容易理解的,心想,很快就可以完结malloc的代码了

hlt,想太多了

看过我之前文章的其实应该还记得,其实每个bin之间是16 byte差距,那么申请的时候,11 byte和12 byte是没差距的,它们最终都会被磨成32 byte。那其实是没差距啊,那为何上面还要排序呢?

unsort第一部分代码记不记得,分割。这就就造成了大块有可能被割裂。这当然是一种节省内存的做法,但另一方面却也增加了代码复杂度。借用以前写英文作文的套路句子。Every coin has two side。

另外,从size链和bin链的关系其实能想到一种数据结构,跳表,一个双层跳表。

unknown part2 - large request


        if (!in_smallbin_range (nb)) {
            bin = bin_at (av, idx);

#define first(bin) (bin)->fd

            /* bin->bk是size最小,fd就是size最大 */
            if ((victim = first (bin)) != bin &&
                (unsigned long) chunksize_nomask (victim) >= (unsigned long) (nb)) {

                victim = victim->bk_nextsize;

                /* 用size查找最适合 - best fit */
                while (((unsigned long) (size = chunksize (victim)) <
                            (unsigned long) (nb)))
                    victim = victim->bk_nextsize;

                /* 不挪走当前size的第一块是为了避免重新构造skip list */
                if (victim != last (bin) &&
                        chunksize_nomask (victim) == chunksize_nomask (victim->fd))
                    victim = victim->fd;

                remainder_size = size - nb;

                /* 从size链和bin链上取下来,并重新构建skip list */
                unlink (av, victim, bck, fwd);

                /* 不能再分割的情况 */
                if (remainder_size < MINSIZE) {
                    set_inuse_bit_at_offset (victim, size);
                }

                /* 大块分割 */
                else {
                    /* 以下这段代码,估计你看到过很多次了,我就懒得写注释了 */
                    remainder = chunk_at_offset (victim, nb);

                    bck = unsorted_chunks (av);
                    fwd = bck->fd;

                    remainder->bk = bck;
                    remainder->fd = fwd;
                    bck->fd = remainder;
                    fwd->bk = remainder;

                    if (!in_smallbin_range (remainder_size)) {
                        remainder->fd_nextsize = NULL;
                        remainder->bk_nextsize = NULL;
                    }

                    set_head (victim, nb | PREV_INUSE |
                            (av != &main_arena ? NON_MAIN_ARENA : 0));
                    set_head (remainder, remainder_size | PREV_INUSE);
                    set_foot (remainder, remainder_size);
                }

                return chunk2mem (victim);
            }
        }

unknown part3 - bit map


        /* 当前索引找不到,+1 */
        ++idx;
        bin = bin_at (av, idx);

        /* 具体这个bit map就是,每一个bin如果有内存块就把对应位设为1 */
        unsigned int block = idx2block (idx);
        unsigned int map = av->binmap[block];
        unsigned int bit = idx2bit (idx);

        for (;; ) {
            /* 如果当前bit的数值已经大于map,或者bit为0 */
            if (bit > map || bit == 0) {
                /* 一直搜索,直到某个map里面存在非空bin */
                do {
                    if (++block >= BINMAPSIZE) /* out of bins */
                        goto use_top;

                } while ((map = av->binmap[block]) == 0);

                bin = bin_at (av, (block << BINMAPSHIFT));
                bit = 1;
            }

            /* 找到被设置的bin的bit */
            while ((bit & map) == 0) {
                bin = next_bin (bin);
                bit <<= 1;
                assert (bit != 0);
            }

            victim = last (bin);

            /* 如果这个bin是空的,清掉bit位 */
            if (victim == bin) {
                av->binmap[block] = map &= ~bit;
                bin = next_bin (bin);
                bit <<= 1;

            } else {
                size = chunksize (victim);
                remainder_size = size - nb;

                /* 取下来 */
                unlink (av, victim, bck, fwd);

                /* 耗尽 */
                if (remainder_size < MINSIZE) {
                    set_inuse_bit_at_offset (victim, size);
                }

                /* 分割 */
                else {
                    remainder = chunk_at_offset (victim, nb);

                    /* 将分割后的块丢到unsort里面去 */
                    bck = unsorted_chunks (av);
                    fwd = bck->fd;

                    remainder->bk = bck;
                    remainder->fd = fwd;
                    bck->fd = remainder;
                    fwd->bk = remainder;

                    /* 如果请求是小块内存,把被分割设置成最后被分割块 */
                    if (in_smallbin_range (nb))
                        av->last_remainder = remainder;

                    if (!in_smallbin_range (remainder_size)){
                        remainder->fd_nextsize = NULL;
                        remainder->bk_nextsize = NULL;
                    }

                    set_head (victim, nb | PREV_INUSE |
                            (av != &main_arena ? NON_MAIN_ARENA : 0));
                    set_head (remainder, remainder_size | PREV_INUSE);
                    set_foot (remainder, remainder_size);
                }

                return  chunk2mem(victim);
            }
        }

到此,unknown部分已经完全讲解完毕。

总结


下回分解!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值