malloc函数详解 glibc2.27

malloc 函数分析(glibc.2.27)

本人菜🐔一只,如果分析的有错误,请大佬指正。

__libc_malloc函数分析

void * __libc_malloc (size_t bytes)
{
    mstate ar_ptr;
    void *victim;
    //把全局变量__malloc_hook赋给了hook,如果hook不为空,则执行hook。
    void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook);
    if (__builtin_expect (hook != NULL, 0))
    	return (*hook)(bytes, RETURN_ADDRESS (0));
    
#if USE_TCACHE
    
    size_t tbytes;
    checked_request2size (bytes, tbytes);//把bytes转换为tcache bytes
    size_t tc_idx = csize2tidx (tbytes);//获取tbytes对应的tcache的tc_idx
    
    MAYBE_INIT_TCACHE ();//如果tcache为NULL,初始化tcache
    
    DIAG_PUSH_NEEDS_COMMENT;
    //如果tc_idx合法,且对应的tcache不为空,则直接返回该tcahce中的第一个chunk(与fastbin类似)
    if (tc_idx < mp_.tcache_bins
        && tcache
        && tcache->entries[tc_idx] != NULL)
    {
        return tcache_get (tc_idx);
    }
    DIAG_POP_NEEDS_COMMENT;
#endif

    if (SINGLE_THREAD_P)//如果是单线程
    {
        //调用_int_malloc,实现内存分配
        victim = _int_malloc (&main_arena, bytes);
        /*
            #define arena_for_chunk(ptr) \
            (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)

                过以下检测需要满足的要求,只需满足一条即可
                1. victim 为 0
                2. IS_MMAPPED 为 1
                3. NON_MAIN_ARENA 为 0
        */
        assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
                &main_arena == arena_for_chunk (mem2chunk (victim)));
        
        return victim;
    }
    
    //获取当前的arena,如果是主线程则获得的是main_arena
    arena_get (ar_ptr, bytes);
	//调用_int_malloc,实现内存分配
    victim = _int_malloc (ar_ptr, bytes);
	//如果_int_malloc 分配失败,并且我们之前能够找到一个可用arena,可以用另一个arena重试。
    if (!victim && ar_ptr != NULL)
    {
        LIBC_PROBE (memory_malloc_retry, 1, bytes);
        ar_ptr = arena_get_retry (ar_ptr, bytes);
        victim = _int_malloc (ar_ptr, bytes);
    }
	//释放mutex引用的互斥锁对象,因为ptmalloc支持多线程
    if (ar_ptr != NULL)
        __libc_lock_unlock (ar_ptr->mutex);
    /*
        #define arena_for_chunk(ptr) \
        (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)

            过以下检测需要满足的要求,只需满足一条即可
            1. victim 为 0
            2. IS_MMAPPED 为 1
            3. NON_MAIN_ARENA 为 0
    */
    assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
            ar_ptr == arena_for_chunk (mem2chunk (victim)));
    return victim;
}

__int_malloc函数分析

  1. REMOVE_FB的功能是取出空闲chunk

    #define REMOVE_FB(fb, victim, pp)//摘除一个空闲chunk
    do
    {							
        victim = pp;					
        if (victim == NULL)				
        break;						
    }							
    while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) != victim);
    //catomic_compare_and_exchange_val_rel_acq 功能是 如果*fb等于victim,则将*fb存储为victim->fd,返回victim;
    //其作用是从刚刚得到的空闲chunk链表指针中取出第一个空闲的chunk(victim),并将链表头设置为该空闲chunk的下一个chunk(victim->fd)
    
  2. 如果需要分配的内存大小nb落在fastbin的范围内,尝试从 fast bins 中 分配 chunk

    //get_max_fast返回fastbin可以存储内存的最大值,它在ptmalloc的初始化函数malloc_init_state中定义。
    if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
    {
        idx = fastbin_index (nb);//获得chunk大小nb对应的fastbin索引。
        mfastbinptr *fb = &fastbin (av, idx);//通过fastbin取出空闲chunk链表头指针
        mchunkptr pp;
        victim = *fb;//获取对应大小的fastbin的链表中的第一个空闲的chunk
    
        if (victim != NULL)//如果取到了第一个chunk
        {
            if (SINGLE_THREAD_P) //判断是否单线程程序
                *fb = victim->fd;//摘除一个空闲chunk
            else
                REMOVE_FB (fb, pp, victim);//摘除一个空闲chunk
    
            if (__glibc_likely (victim != NULL))
            {
                //由取出的chunk的size计算出来的idx要等于bin的idx
                //就是检查拿到的chunk的size是否符合该fastbin的大小
                size_t victim_idx = fastbin_index (chunksize (victim));
                if (__builtin_expect (victim_idx != idx, 0))
                    malloc_printerr ("malloc(): memory corruption (fast)");
    
                check_remalloced_chunk (av, victim, nb);//什么也没实现
    
                #if USE_TCACHE
    
                size_t tc_idx = csize2tidx (nb);//获取对应大小的tcache的idx
                //如果tchache初始化了,且tc_idx合法
                if (tcache && tc_idx < mp_.tcache_bins)
                {
                    mchunkptr tc_victim;
    
                    /*
                    	如果tcache没有装满,且fastbin 中还有空闲的chunk,则把fastbin中的chunk
                        加入到tcache中,直到tcache满了或者fastbin中不存在chunk
                    */
                    while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = *fb) != NULL)
                    {
                        if (SINGLE_THREAD_P)//判断是否单线程程序
                            *fb = tc_victim->fd;//取出一个空闲chunk
                        else
                        {
                            REMOVE_FB (fb, pp, tc_victim);//取出一个空闲chunk
                            if (__glibc_unlikely (tc_victim == NULL))
                                break;
                        }
                        /*
                                static __always_inline void  \
                                tcache_put (mchunkptr chunk, size_t tc_idx)
                                {
                                    tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
                                    assert (tc_idx < TCACHE_MAX_BINS);
                                    e->next = tcache->entries[tc_idx];
                                    tcache->entries[tc_idx] = e;
                                    ++(tcache->counts[tc_idx]);
                                }
                        */
    
                        tcache_put (tc_victim, tc_idx);//把fastbin中拿出的chunk加入到tcache链表中
                    }
                }
                #endif
                void *p = chunk2mem (victim);//把chunk的指针转换成mem的指针
                alloc_perturb (p, bytes);//将p的mem部分全部设置为perturb_byte ,默认什么也不做
                return p;
            }
        }
    }
    
  3. 如果需要分配的内存大小nb落在small bin的范围内,尝试从 small bin中 分配 chunk

    if (in_smallbin_range (nb))
    {
        idx = smallbin_index (nb);//获取nb对应大小的small bin的idx
        bin = bin_at (av, idx);//获取idx对应的small bin
    
        if ((victim = last (bin)) != bin)//如果对应的small bin 非空
        {
            bck = victim->bk; //获得vitcm的后一个chunk
    
            if (__glibc_unlikely (bck->fd != victim))//检查small bin链表的完整性
                malloc_printerr ("malloc(): smallbin double linked list corrupted");
    
            //设置victim物理相邻的下一个chunk的prev_inuse位
            set_inuse_bit_at_offset (victim, nb);
    
            bin->bk = bck;
            bck->fd = bin;//把victim从small bin中移除
    
            if (av != &main_arena)
                set_non_main_arena (victim);//如果不是主线程则设置NON_MAIN_ARENA位
    
            check_malloced_chunk (av, victim, nb);//默认不做任何操作
    
            #if USE_TCACHE
    
            size_t tc_idx = csize2tidx (nb);
            if (tcache && tc_idx < mp_.tcache_bins)
            {
                mchunkptr tc_victim;
                /*
                    	如果tcache没有装满,且small bin 中还有空闲的chunk,则把small bin中的chunk
                        加入到tcache中,直到tcache满了或者small bin中不存在chunk
               	*/
    
                while (tcache->counts[tc_idx] < mp_.tcache_count&& (tc_victim = last (bin)) != bin)
                {
                    if (tc_victim != 0)
                    {
                        //获得tc_vitcm的后一个chunk
                        bck = tc_victim->bk;
                        //设置tc_victim物理相邻的下一个chunk的prev_inuse位
                        set_inuse_bit_at_offset (tc_victim, nb);
                        
                        if (av != &main_arena)
                            set_non_main_arena (tc_victim);//如果不是主线程则设置NON_MAIN_ARENA位
                        bin->bk = bck;
                        bck->fd = bin;
                        
                        tcache_put (tc_victim, tc_idx);//把small bin中拿出的chunk加入到tcache链表中
                    }
                }
            }
            #endif
            void *p = chunk2mem (victim);//把chunk的指针转换成mem的指针
            alloc_perturb (p, bytes);//将p的mem部分全部设置为perturb_byte ,默认什么也不做
            return p;
        }
    }
    
  4. 如果我们申请的chunk的大小不在small bin的范围,也就是说我们申请的chunk的大小在large bin的范围,如果fastbin中存在chunk,我们在这一步会遍历fastbin,可以合并的块合并,然后加入到unsorted bin中,如果与topchunk相邻则直接合并到top chunk(注意这里是把fastbin中的所有块清空)

    else
    {
        idx = largebin_index (nb);
        if (atomic_load_relaxed (&av->have_fastchunks))
            malloc_consolidate (av);//合并fastbin中的空闲chunk
    }
    
    
    #if USE_TCACHE
    INTERNAL_SIZE_T tcache_nb = 0;
    size_t tc_idx = csize2tidx (nb);//获取nb对应大小的tcache的idx
    if (tcache && tc_idx < mp_.tcache_bins)
        tcache_nb = nb;
    int return_cached = 0;
    tcache_unsorted_count = 0;
    #endif
    
  5. 接着我们遍历unsorted bin,寻找合适的chunk.

    //如果unsorted bins不为空,从尾到头遍历unsorted bin中的每个chunk
    while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
    {
        bck = victim->bk;
        if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
            || __builtin_expect (chunksize_nomask (victim)> av->system_mem, 0))
            malloc_printerr ("malloc(): memory corruption");
        size = chunksize (victim);
    
    
    	/*
            如果要申请的大小在smallbin范围 且 unsorted chunks 只有一个chunk,且
            victim是last_remainder 且 victim的size大于请求的chunk的大小nb加上
            (MINSIZE)最小chunk的size,那么就切割remainder,然后返回victim。
            
            last_remainder 是一个 chunk 指针,分配区上次分配 small chunk 时,
            从一个 chunk 中分 裂出一个 small chunk 返回给用户,分裂后的剩余部分
            形成一个 chunk,last_remainder 就是 指向的这个 chunk。
        */
        if (in_smallbin_range (nb) &&
            bck == unsorted_chunks (av) &&
            victim == av->last_remainder &&
            (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
        {
         	//分割remainder
            remainder_size = size - nb;//计算分割后剩下的size
            remainder = chunk_at_offset(victim, nb);//获取remainder的地址
            //把remainder加入unsorted bin中
            unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
            av->last_remainder = remainder; // 设置last_remainder为remainder
            remainder->bk = remainder->fd = unsorted_chunks(av);
            //如果是remainder在large bin的范围,则把fd_nextsize,fd_nextsize清零
            if (!in_smallbin_range(remainder_size)) {
                remainder->fd_nextsize = NULL;
                remainder->fd_nextsize = NULL;
            }
    		//设置victim的size
            set_head(victim, nb | PREV_INUSE |
                     (av != &main_arena ? NON_MAIN_ARENA : 0));
            //设置remainder的size
            set_head(remainder, remainder_size | PREV_INUSE);
            //设置remainder的物理相邻的下一个chunk的prev_size
            set_foot(remainder, remainder_size);
    
            check_malloced_chunk(av, victim, nb);//默认不做任何操作
            void *p = chunk2mem(victim);//将chunk指针转化为mem指针
            alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
            return p;
        }
    
        //把victim从unsorted bin 中移除
        unsorted_chunks (av)->bk = bck;
        bck->fd = unsorted_chunks (av);
    
        //如果 victim 的size 与申请的size相等
        if (size == nb)
        {
            //设置victim物理相邻的下一个chunk的prev_inuse位
            set_inuse_bit_at_offset (victim, size);
            //如果av不是main_arena 也就是说如果不是主进程,设置NON_MAIN_ARENA位
            if (av != &main_arena)
                set_non_main_arena (victim);
            #if USE_TCACHE
           
            //如果对应的tcache没有满,则把他加入到tcache中,设置return标志
            if (tcache_nb && tcache->counts[tc_idx] < mp_.tcache_count)
            {
                tcache_put (victim, tc_idx);
                return_cached = 1;
                continue;
            }
            else//如果对应的tcache满了,则直接返回。
            {
                #endif
                check_malloced_chunk(av, victim, nb); // 默认不做任何操作
                void *p = chunk2mem(victim);//把chunk转换为mem指针
                alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
                return p;
                #if USE_TCACHE
            }
            #endif
        }
    
        //如果在smallbin的范围,则放到对应的small bin中
        if (in_smallbin_range (size))
        {
            victim_index = smallbin_index(size);//获取size对应的smallbin的index
            bck = bin_at(av, victim_index);//bck指向size对应的smallbin的链表头
            //fwd指向size对应的smallbin的链表中的新加入的chunk(small bin使用头插法)
            fwd = bck->fd;
        }
        else//如果不在smallbin的范围,也就是说在large bin 的范围
        {
            victim_index = largebin_index(size);//获取size对应的large bin的index
            bck = bin_at(av, victim_index);//bck指向size对应的large bin的链表头
            fwd = bck->fd;//fwd指向size对应的large bin的链表中的新加入的chunk
            
            //如果large bin 非空,在largbin进行按顺序插入
           	if (fwd != bck) {
            	size |= PREV_INUSE;
                assert((bck->bk->size & NON_MAIN_ARENA) == 0);
                /*
                	large bin中的chunk是按从大到小排列的,如果size < large bin 
                	的最后一个chunk,说明size是这个large bin中的最小的,我们把它
                	加入到此large bin尾部。
                */
                if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)) 
                {
                    
                    fwd = bck;
                    bck = bck->bk;
                    
                    /*
                    large bin 中size最小的chunk的fd_nextsize会指向size最大的
                    那个chunk,也就是首部的chunk。同样,large bin 中size最大的
                    chunk的bk_nextsize会指向size最小的那个chunk。
                    victim的bk_nextsize指向large bin原来最小的chunk,它的
                    bk_nextsize指向最大的那个chunk。那么原来的最小的就成了第二小的了。
                    把它fd_nextsize和bk_nextsize都修正。
                    */
                    victim->fd_nextsize = fwd->fd;
                    victim->bk_nextsize = fwd->fd->bk_nextsize;
                    //最大size的chunk的bk_nextsize,和原来最小chunk的bk_nextsize都指向victim
                    fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
                } 
                else //如果victim不是large bin 中最小的chunk
                {
                    //检查NON_MAIN_ARENA位是否为0
                    assert (chunk_main_arena (fwd));
                    //从大到小(从头到尾)找到合适的位置
                    while ((unsigned long) size < chunksize_nomask (fwd))
                    {
                        fwd = fwd->fd_nextsize;
                        assert (chunk_main_arena (fwd));
                    }
    				//如果size刚好相等,就直接加入到其后面省的改fd_nextsize和bk_nextsize了
                   if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
                       fwd = fwd->fd;
                   else 
                   {
                       //size不相等,即size>fwd->size,把victim加入到纵向链表中
                       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 为空,将victim加入到纵向列表
            	victim->fd_nextsize = victim->bk_nextsize = victim;
        }
    
        //#define mark_bin(m, i)    ((m)->binmap[idx2block (i)] |= idx2bit (i))
        mark_bin(av, victim_index); //把victim加入到的bin的表示为非空
        //把victim加入到large bin的链表中
        victim->bk = bck;
        victim->fd = fwd;
        fwd->bk = victim;
        bck->fd = victim;
    
    
        #if USE_TCACHE
        ++tcache_unsorted_count;
        //若达到tcache_unsorted_limit限制且之前已经存入过chunk则在此时取出(默认无限制)
        if (return_cached
            && mp_.tcache_unsorted_limit > 0
            && tcache_unsorted_count > mp_.tcache_unsorted_limit)
        {
            return tcache_get (tc_idx);//返回size对应的tcache的第一个chunk
        }
        #endif
    
        #define MAX_ITERS       10000
        if (++iters >= MAX_ITERS)
            break;
    }
    
  6. 如果unsorted bin中也找不到合适的chunk,且在large bin的范围,继续在largebin中找

    #if USE_TCACHE
    //如果上面没有取出,且之前已经存入过chunk则在此时取出
    if (return_cached)
    {
        return tcache_get (tc_idx);
    }
    #endif
    
    
    //如果unsorted bin中也找不到合适的chunk,且在large bin的范围,继续在largebin中找
    if (!in_smallbin_range (nb))
    {
        bin = bin_at (av, idx);
    
         //如果对应的 bin 不为空 且 其中最大的chunk也很比我们想要的nb大
        if ((victim = first (bin)) != bin
            && (unsigned long) chunksize_nomask (victim)
            >= (unsigned long) (nb))
        {
            // 反向遍历nextsize纵向链表(从小到大),找到第一个比size大的chunk
            victim = victim->bk_nextsize;
            while (((unsigned long) (size = chunksize (victim)) <
                    (unsigned long) (nb)))
                victim = victim->bk_nextsize;
    
           /*
                如果取出的chunk不是bin的最后一个chunk,同时该chunk有大小相同的chunk连接在一起
                它就会取它前面的那个chunk即 chunk->fd ,因为大小相同的chunk只有一个会被串在
                nextsize链上这可以避免额外的bk_nextsize和fd_nextsize的赋值
            */
            if (victim != last (bin)
                && chunksize_nomask (victim)
                == chunksize_nomask (victim->fd))
                victim = victim->fd;
    
            remainder_size = size - nb;//计算切割后的大小
            unlink(av, victim, bck, fwd); //通过unlink将chunk从链表移除
    
    
        
    
            if (remainder_size < MINSIZE) {
                //如果切割后的大小不足以作为一个chunk,那么就不分割直接将其标志位设为inuse
                //如果不是main_arena,同时设置NO_main_arena标志位
                set_inuse_bit_at_offset(victim, size);
                if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
            }
        	else 
            {
                //如果剩余的大小可以作为一个chunk
                //获得剩余部分的地址,放入unsorted bin中
                remainder = chunk_at_offset (victim, nb);
                bck = unsorted_chunks (av);
                fwd = bck->fd;
                if (__glibc_unlikely (fwd->bk != bck))
                    malloc_printerr ("malloc(): corrupted unsorted chunks");
                remainder->bk = bck;
                remainder->fd = fwd;
                bck->fd = remainder;
                fwd->bk = remainder;
                //如果剩余部分的大小在largin bin的范围,则清空nextsize字段
                if (!in_smallbin_range (remainder_size))
                {
                    remainder->fd_nextsize = NULL;
                    remainder->bk_nextsize = NULL;
                }
               //设置victim的size
                set_head(victim, nb | PREV_INUSE |
                         (av != &main_arena ? NON_MAIN_ARENA : 0));
                //设置remainder的size
                set_head(remainder, remainder_size | PREV_INUSE);
                //设置remainder的物理相邻的下一个chunk的prev_size
                set_foot(remainder, remainder_size);
             
            }
            check_malloced_chunk(av, victim, nb);//默认不做任何操作
            void *p = chunk2mem(victim);//将chunk指针转化为mem指针
            alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
            return p;
        }
    }
    
  7. 如果通过上面的方式从最合适的 small bin 或 large bin 中都没有分配到需要的 chunk,则 查看比当前 bin 的 index 大的 small bin 或 large bin 是否有空闲 chunk 可利用来分配所需的 chunk。

    /*
    获取下一个相邻 bin 的空闲 chunk 链表,并获取该 bin 对应 binmap 中的 bit 位的值。binmap 字段是一个 int 数组,ptmalloc 用一个 bit 来标识该 bit 对应的 bin 中是否包含空闲 chunk。Binmap 按 block 管理,每个 block 为一个 int,共 32 个 bit,可以表示 32 个 bin 中是否有空闲 chunk 存在。使用 binmap 可以加快查找 bin 是否包含空闲 chunk。这里只查询比所需 chunk 大的 bin 中是否有空闲 chunk 可用。
    */
    
    ++idx;
    bin = bin_at(av, idx);//获取当前bin的下一个bin
    block = idx2block(idx);
    map = av->binmap[block];//获取block
    bit = idx2bit(idx);//获取block中对应的bit
    
    /*
    	Idx2bit()宏将 idx 指定的位设置为 1,其它位清零,map 表示一个 block(unsigned int) 值,如果bit 大于 map,意味着比bit对应的bin的size大的bin中无空闲chunk,如果 map 为 0,该 block 所对应的所有 bins 中都没有空闲 chunk, 于是遍历 binmap 的下一个 block,直到找到一个不为 0 的 block 或者遍历完所有的 block。 退出循环遍历后,设置 bin 指向 block 的第一个 bit 对应的 bin,并将 bit 置为 1,表示该 block 中 bit 1 对应的 bin,这个 bin 中如果有空闲 chunk,该 chunk 的大小一定满足要求。
    */
    
    for (;;) 
    {
        /*
        	如果bit 大于 map,意味着比该bit对应的bin的size大的bin中无空闲chunk,如果 map 为
            0,该 block 所对应的所有 bins 中都没有空闲 chunk 。接着在下一个block中寻找
        */
        if (bit > map || bit == 0) 
        {
            do {
             //如果block超过了范围,说明比所需chunk大的bin中没有chunk,直接使用top_chunk
                if (++block >= BINMAPSIZE) /* out of bins */
                    goto use_top;
             //如果block为0,这表明block中的所有bit所对应的bin没有空闲chunk
            } while ((map = av->binmap[block]) == 0);
    	
            bin = bin_at(av, (block << BINMAPSHIFT));
            bit = 1;
        }
    
        
    /*
    在一个block遍历对应的 bin,直到找到一个 bit 不为 0 退出遍历,则该 bit 对于的 bin 中有空闲 chunk 存在。
    */  
        while ((bit & map) == 0) {
            bin = next_bin(bin);
            bit <<= 1;
            assert(bit != 0);
        }
    
        //获取bin尾部的chunk
        victim = last(bin);
    
        /*
            如果 victim 与 bin 链表头指针相同,表示该 bin 中没有空闲 chunk,binmap 中的相应位 
            设置不准确,将 binmap 的相应 bit 位清零,获取当前 bin 下一个 bin,将 bit 移到下一个 
            bit 位,即乘以 2。
        */
        if (victim == bin) {
            av->binmap[block] = map &= ~bit; /* Write through */
            bin = next_bin(bin);
            bit <<= 1;
        }
        else 
        {
            /*
                当前 bin 中的最后一个 chunk 满足要求,获取该 chunk 的大小,计算切分出所需 chunk 
                后剩余部分的大小,然后将 victim 从 bin 的链表中取出。
            */
            size = chunksize(victim);
    
            assert((unsigned long) (size) >= (unsigned long) (nb));
    
            remainder_size = size - nb;//计算分割后的大小
    
            unlink(av, victim, bck, fwd);//从bin中取出victim
    
            /*
            如果剩余部分的大小小于 MINSIZE,将整个 chunk 分配给应用层,设置 victim 的状态为
            inuse,如果当前分配区为非主分配区,设置 victim 的非主分配区标志位。
            */
            if (remainder_size < MINSIZE) {
                set_inuse_bit_at_offset(victim, size);
                if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
            }
            else 
            {
                //如果剩余的大小可以作为一个chunk
                //获得剩余部分的地址,放入unsorted bin中
                remainder = chunk_at_offset(victim, nb);
                bck = unsorted_chunks(av);
                fwd = bck->fd;
                //这里检查unsorted bin 中的链表头部是否合法
                if (__glibc_unlikely(fwd->bk != bck)) {
                   malloc_printerr ("malloc(): corrupted unsorted chunks 2");
                }
                remainder->bk = bck;
                remainder->fd = fwd;
                bck->fd = remainder;
                fwd->bk = remainder;
    
          		//设置last_remainder为刚刚分割剩余的remainder
                if (in_smallbin_range(nb))
                    av->last_remainder = remainder;
                
                 //如果剩余部分的大小在largin bin的范围,则清空nextsize字段
                if (!in_smallbin_range(remainder_size)) {
                    remainder->fd_nextsize = NULL;
                    remainder->bk_nextsize = NULL;
                }
                
                
                //设置victim的size
                set_head(victim, nb | PREV_INUSE |
                         (av != &main_arena ? NON_MAIN_ARENA : 0));
                //设置remainder的size
                set_head(remainder, remainder_size | PREV_INUSE);
                //设置remainder的物理相邻的下一个chunk的prev_size
                set_foot(remainder, remainder_size);
            }
            
            check_malloced_chunk(av, victim, nb);//默认不做任何操作
            void *p = chunk2mem(victim);//将chunk指针转化为mem指针
            alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
            return p;
        }
    }
    
  8. 如果从所有的 bins 中都没有获得所需的 chunk,可能的情况为 bins 中没有空闲 chunk, 或者所需的 chunk 大小很大,下一步将尝试从 top chunk 中分配所需 chunk。

    use_top:
    
    //如果以上都无法满足我们要申请的chunk的要求,最后使用top_chunk
    victim = av->top; // victim 指向top chunk
    size = chunksize(victim);//获取top chunk的size
    //如果top chunk 满足我们要申请的chunk大小要求(top chunk 的size > 我们要申请的chunk + 最小chunk的size ),那么就分割它。
    if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 
    {
        remainder_size = size - nb;
        remainder = chunk_at_offset(victim, nb);
        av->top = remainder;
        set_head(victim, nb | PREV_INUSE |
                 (av != &main_arena ? NON_MAIN_ARENA : 0));
        set_head(remainder, remainder_size | PREV_INUSE);
    
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p;
    }
    
    //如果top chunk 空间不够,且fastbin中有空闲chunk
    //则触发malloc_consolidate函数合并fastbin中能合并chunk,然后加入到unsorted bin中,如果是与top_chunk相邻的chunk则直接与top_chunk合并,
    //接着返回for循环开始,从unsorted bin 中继续查找
    else if (have_fastchunks(av)) {
        malloc_consolidate(av);
        /* restore original bin index */
        if (in_smallbin_range(nb))
            idx = smallbin_index(nb);
        else
            idx = largebin_index(nb);
    }
    //如果top chunk 空间不够,且fastbin中没有空闲chunk,就通过sysmalloc从操作系统分配内存。
    else {
        void *p = sysmalloc(nb, av); //这里也是house of orange利用的地方
        if (p != NULL)
            alloc_perturb(p, bytes);//将p的mem部分全部设置为bytes ,默认什么也不做
        return p;
    }
    

我们总结一下 malloc 响应用户内存分配要求的具体步骤:

  1. 将用户的请求大小转换为实际需要分配的 chunk 空间大小。

  2. 获取对应大小的tcache的tc_idx,如果tc_idx合法,且对应的tcache不为空,则直接返回该tcahce中的第一个chunk,分配结束,否则进入下一步。

  3. 判断所需分配 chunk 的大小是否满足 chunk_size <= max_fast (max_fast 默认为 64B)。如果是的话,尝试在 fast bins 中取一个所需大小的 chunk 分配给用户。如果可以找到,取出这个chunk返回,把剩下的chunk加入到tcache中,直到tcache满了或者fastbin中不存在chunk,分配结束,否则跳到下一步。

  4. 判断所需大小是否处在 small bins 中,如果chunk 大小处在 small bins 中,转到下一步,否则跳到第6步。

  5. 根据所需分配的 chunk 的大小,找到具体所在的某个 small bin,尝试从该 bin 的尾部摘取一个恰好满足大小的 chunk,若成功,取出这个chunk返回,把剩下的chunk加入到tcache中,并设置物理相邻的下一个chunk的prev_inuse位,直到tcache满了或者small bin中不存在chunk,分配结束,否则转到第7步。

  6. 如果fastbin中存在空闲chunk,遍历 fast bins 中的 chunk,将相邻的 chunk 进行合并, 并链接到 unsorted bin 中。然后转到下一步。

  7. 到了这一步,说明需要分配的是一块大的内存,或者 tcache,fastbin和small bins 中找不到合适的 chunk,于是遍历 unsorted bin 中的 chunk。

    如果 unsorted bin只有一个 chunk,并且这个 chunk 在上次分配时被分割过,并且所需分配的 chunk 大小属于 small bins,并且 chunk 的大小大于等于需要分配的大小,这种情况下就直 接将该 chunk 进行切割,分配结束。

    若chunk的size刚好满足大小,且对应的tcache没有满,则把他加入到tcache中,并跳出这一次的循环继续遍历unsorted bin,如果对应的tcache满了则直接返回,分配结束。

    如果以上都不满足,将根据 chunk 的空间大小将其放入 small bins 或是 large bins 中。

    如果达到tcache_unsorted_limit限制(默认无限制)且之前已经存入过chunk则在此时,取出size对应的tcache的第一个chunk返回,否则转入下一步。

  8. 如果之前已经存入过chunk且上一步没取出,则在此时取出返回我们申请的size对应的tcache的第一个chunk,分配结束。否则进入下一步。

  9. 到了这一步,说明需要分配的是一块大的内存,或者tcache, fastbin,small bins 和 unsorted bin 中都找不到合适的 chunk。如果需要分配的是一块大的内存,则跳到下一步,否则跳到第11步

  10. 从 large bins 中按照“smallest-first,best-fit”原则,找一个合适的 chunk,从 中划分一块所需大小的 chunk,并将剩下的部分放入 unsorted bin中 。若操作成功,则 分配结束,否则跳到下一步。

  11. 如果通过上面的方式从最合适的fastbin,small bin 或 large bin 中都没有分配到需要的 chunk,则 查看比当前 最合适bin 的 index 大的 small bin 或 large bin 是否有空闲 chunk 可利用,来分割得到所需的 chunk,并将剩下的部分放入 unsorted bin中。若操作成功,则 分配结束,否则跳到下一步。

  12. 如果以上都无法满足我们要申请的chunk的要求,那么就需要操作 top chunk 来 进行分配了。判断 top chunk 大小是否满足所需 chunk 的大小,如果是,则从 top chunk 中分出一块来。否则转到下一步。

  13. 到了这一步,说明 top chunk 也不能满足分配要求。如果fastbin中有空闲chunk则触发malloc_consolidate函数合并fastbin中能合并chunk,然后加入到unsorted bin中,如果是与top_chunk相邻的chunk则直接与top_chunk合并。若操作成功,则 跳转到 7,否则转到下一步

  14. 到了这一步,说明,top chunk 也不能满足分配要求,且fastbin中也没有空闲chunk。通过sysmalloc从操作系统分配内存。

posted @ 2020-06-29 11:09  Rookle  阅读(576)  评论(0编辑  收藏  举报