printf("Hello World~~");|

c3n1g

园龄:1年6个月粉丝:2关注:0

2024-09-24 22:41阅读: 9评论: 0推荐: 0

malloc分析

大致流程

__libc_malloc

我们平时所用的malloc函数在glibc中实际调用的是__libc_malloc函数,通过string_alias宏强命名为malloc使用的。__libc_malloc函数主要操作为

  1. 判断__malloc_hook是否有指定hook,有则直接调用__malloc_hook
  2. 如果没有设置__malloc_hook,则获取当前线程的堆分配器,并加锁
  3. 调用_int_malloc函数获取chunk
  4. 对分配的chunk进行校验处理并返回
void *
__libc_malloc (size_t bytes)
{
    mstate ar_ptr;
    void *victim;

    void *(*hook) (size_t, const void *)
        = atomic_forced_read (__malloc_hook); // 获取__malloc_hook
    if (__builtin_expect (hook != NULL, 0)) 
        return (*hook)(bytes, RETURN_ADDRESS (0));  // 执行__malloc_hook

    arena_get (ar_ptr, bytes);  // 获取当前线程的堆分配器并加锁

    victim = _int_malloc (ar_ptr, bytes); // 进行分配

    if (!victim && ar_ptr != NULL)    // 如果获取chunk失败,并且分配器不为空
    {
        LIBC_PROBE (memory_malloc_retry, 1, bytes);
        ar_ptr = arena_get_retry (ar_ptr, bytes);   // 尝试重新获取堆分配器
        victim = _int_malloc (ar_ptr, bytes);   // 再次分配
    }

    if (ar_ptr != NULL) // 释放分配器锁
        (void) mutex_unlock (&ar_ptr->mutex);

    // 断言获取的chunk是否为空、chunk是否为mmap分配、分配器是否匹配
    assert (!victim || chunk_is_mmapped (mem2chu(victim)) ||
          ar_ptr == arena_for_chunk (mem2chunk (victim)));
    return victim;
}

_int_malloc

实际上所有的分配逻辑都是在_int_malloc中进行处理的,它分为几个处理过程

  1. 如果堆分配器为空,则直接通过sysmalloc分配
  2. 从fastbin中获取chunk
  3. 从smallbin中获取chunk
  4. 从unsortedbin中获取chunk
  5. 从largebin中获取chunk
  6. 从top chunk中分割chunk

sysmalloc

    checked_request2size (bytes, nb); // 判断申请大小是否合法并转换为chunk大小

    if (__glibc_unlikely (av == NULL))  // 判断堆分配器是否为空
    {
        void *p = sysmalloc (nb, av); // 直接通过sysmalloc分配
        if (p != NULL)    // 分配到chunk
	        alloc_perturb (p, bytes);   // 如果设置perturb_type,则初始化chunk内存为perturb_type ^ 0xff
        return p;
    }

bytes是要申请的内存大小,首先checked_request2size会检查byts是否太大,导致在填充和对齐时将chunk大小绕回零。然后checked_request2size将bytes对齐并转换为chunk大小,也就是将nb对齐内存,然后加上堆块头。

fastbin

    if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))  // 判断nb大小是否小于fastbin最大的大小
    {
        idx = fastbin_index (nb); // 获取nb对应fastbin的index
        mfastbinptr *fb = &fastbin (av, idx); // 获取该bin的指针
        mchunkptr pp = *fb; // 获取该bin中第一个chunk
        do
        {
            victim = pp;
            if (victim == NULL) // fastbin为空或者到链表结尾直接退出
                break;
        }
        while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))
            != victim);  // 将bin中第一个chunk解链

        if (victim != 0)  // 如果bin中不为空的话
        {
            if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))  // 如果这个chunk的大小不符合bin位置,则报错
            {
                errstr = "malloc(): memory corruption (fast)";
                errout:
                malloc_printerr (check_action, errstr, chunk2mem (victim), av);
                return NULL;
            }
            check_remalloced_chunk (av, victim, nb);  // DEBUG模式下进行详细检测
            void *p = chunk2mem (victim); // chunk转mem
            alloc_perturb (p, bytes); // 如果设置了perturb_type,则将mem初始化为perturb_type^0xff
            return p;
        }
    }

get_max_fast宏是获取global_max_fast全局变量的值,代表fastbin最大的大小,只要小于这个值的chunk size都属于fastbin范围。

smallbin

    if (in_smallbin_range (nb)) // 申请大小在small bin范围
    {
        idx = smallbin_index (nb);  // 获取nb对应smallbin中的index
        bin = bin_at (av, idx); // 获取该bin的指针

        if ((victim = last (bin)) != bin) // 获取bin中最后一个chunk(先进先出)
        {
            if (victim == 0)  // smallbin没有初始化(获取到的chunk指针为NULL)
                malloc_consolidate (av);  // 初始化堆管理器, 合并fastbin chunk到unsortedbin
            else
            {
                bck = victim->bk;   // 获取chunk前一个chunk
	            if (__glibc_unlikely (bck->fd != victim)) // 如果前一个chunk的fd不指向当前chunk则报错
                {
                    errstr = "malloc(): smallbin double linked list corrupted";
                    goto errout;
                }
                set_inuse_bit_at_offset (victim, nb); // 设置物理临近的下一个chunk的inuse位
                bin->bk = bck;  // 将chunk解链
                bck->fd = bin;

                if (av != &main_arena)  // 判断是否为主分配器,否则设置标志
                    victim->size |= NON_MAIN_ARENA;
                check_malloced_chunk (av, victim, nb);  // DEBUG模式下详细检测
                void *p = chunk2mem (victim); // chunk转mem
                alloc_perturb (p, bytes); // 如果设置了pertub_type则初始化内存为pertub_type^0xff
                return p;
            }
        }
    }

malloc_consolidate这个操作在很多地方都会被调用到,主要是将fastbin中的chunk合并处理,放入到unsortedbin中或与top chunk合并

unsortedbin

一般进行到这步说明申请的chunk比较大,在largebin范围,首先也会进行合并fastbin的操作

    else
    {
        idx = largebin_index (nb);  // 获取对应largebin的index
        if (have_fastchunks (av)) // 如果fastbin中有chunk
            malloc_consolidate (av);  // 合并fastbin chunk到unsortedbin
    }
        while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av)) // 获取unsortedbin中最后一个chunk(先进先出)
        {
            bck = victim->bk; // 该chunk的前一个chunk
            if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0) // 判断这个chunk的大小是否小于2*SIZE_T,或者大小大于系统分配堆内存
                || __builtin_expect (victim->size > av->system_mem, 0))
                malloc_printerr (check_action, "malloc(): memory corruption",
                                chunk2mem (victim), av);
            size = chunksize (victim);  // 获取chunk大小

            if (in_smallbin_range (nb) &&   // 如果申请大小在smallbin范围
                bck == unsorted_chunks (av) &&  // 同时这个chunk是unsortedbin唯一的chunk
                victim == av->last_remainder && // 并标记为remainder chunk
                (unsigned long) (size) > (unsigned long) (nb + MINSIZE))    // chunk大小大于申请大小
            {
                remainder_size = size - nb; // 计算出remainder chunk分配后剩余大小
                remainder = chunk_at_offset (victim, nb); // 计算出新的remainder chunk指针
                unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;  // 将该chunk加入到unsortedbin
                av->last_remainder = remainder; // 设置分配器last_remainder
                remainder->bk = remainder->fd = unsorted_chunks (av); // 设置该chunk的fd和bk指针加入链表
                if (!in_smallbin_range (remainder_size))  // 如果remainder size不属于smallbin范围,也就是largebin范围
                {
                  remainder->fd_nextsize = NULL;    // 设置fd_nextsize和bk_nextsize为NULL,以供后续largebin使用
                  remainder->bk_nextsize = NULL;
                }

                set_head (victim, nb | PREV_INUSE | // 设置分配出去的chunk标志
                        (av != &main_arena ? NON_MAIN_ARENA : 0));
                set_head (remainder, remainder_size | PREV_INUSE);  // 设置剩余部分chunk的标志
                set_foot (remainder, remainder_size);   // 设置剩余部分chunk的下一个chunk的prev_size为remainder size

                check_malloced_chunk (av, victim, nb);  // DEBUG模式下详细检测
                void *p = chunk2mem (victim);   // chunk转mem
                alloc_perturb (p, bytes);   // 如果设置了pertub_type则初始化内存为pertub_type^0xff
                return p;
            }

            unsorted_chunks (av)->bk = bck; // 将当前chunk解链
            bck->fd = unsorted_chunks (av);

            if (size == nb) // 如果申请大小和chunk size刚好相等
            {
              set_inuse_bit_at_offset (victim, size); // 设置物理相邻的下一个chunk inuse标志
              if (av != &main_arena)  // 设置main_arena标志
                    victim->size |= NON_MAIN_ARENA;
              check_malloced_chunk (av, victim, nb);    // DEBUG模式下详细检测
              void *p = chunk2mem (victim); // chunk转mem
              alloc_perturb (p, bytes); // 如果设置了pertub_type则初始化内存为pertub_type^0xff
              return p;
            }

            if (in_smallbin_range (size)) // 如果chunk大小在smallbin范围
            {
                victim_index = smallbin_index (size); // 获取chunk对应的smallbin index
                bck = bin_at (av, victim_index);    // 获取smallbin指针
                fwd = bck->fd;  // 获取bin中第一个chunk
            }
            else
            {
                victim_index = largebin_index (size); // 获取chunk对应的largebin index
                bck = bin_at (av, victim_index);     // 获取largebin指针
                fwd = bck->fd;  // 获取bin中第一个chunk

                if (fwd != bck) // 如果largebin不为空
                {
                    size |= PREV_INUSE;

                    assert ((bck->bk->size & NON_MAIN_ARENA) == 0);
                    if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) // 如果chunk比largebin中最小的还小
                    {
                        fwd = bck;
                        bck = bck->bk;
                    
                        victim->fd_nextsize = fwd->fd;  // 设置chunk的fd_nextsize为最大chunk
                        victim->bk_nextsize = fwd->fd->bk_nextsize;   // 将chunk的bk_nextsize为原本最小的chunk
                        fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; // 将chunk加入的size链表
                    }
                    else
                    {
                        assert ((fwd->size & NON_MAIN_ARENA) == 0);
                        while ((unsigned long) size < fwd->size)    // 遍历到刚好chunk大于或等于的链表处
                        {
                            fwd = fwd->fd_nextsize; // 下一个更小的chunk
                            assert ((fwd->size & NON_MAIN_ARENA) == 0);
                        }

                        if ((unsigned long) size == (unsigned long) fwd->size)  // 如果刚好等于这个chunk
                            fwd = fwd->fd;  // 将要把chunk插入到这个chunk后面
                        else
                        {
                            victim->fd_nextsize = fwd;  // 将chunk加入size链表中该chunk前面
                            victim->bk_nextsize = fwd->bk_nextsize;
                            fwd->bk_nextsize = victim;
                            victim->bk_nextsize->fd_nextsize = victim;
                        }
                        bck = fwd->bk;
                    }
                }
                else
                    victim->fd_nextsize = victim->bk_nextsize = victim; // 直接将chunk加入到size链表中
            }

            mark_bin (av, victim_index);  // 设置bitmap表
            victim->bk = bck;   //NOTE - 将chunk链入bin中
            victim->fd = fwd;
            fwd->bk = victim;
            bck->fd = victim;
            #define MAX_ITERS       10000
            if (++iters >= MAX_ITERS)   // 最多循环次数
                break;
        }

        if (!in_smallbin_range (nb))    // 如果申请大小大于smallbin
        {
            bin = bin_at (av, idx); // 获取largbin对应的指针

            if ((victim = first (bin)) != bin &&  // 如果bin不为空,或者bin中最大chunk大于申请大小
                (unsigned long) (victim->size) >= (unsigned long) (nb))
            {
                victim = victim->bk_nextsize;   // 获取bin中最小chunk
                while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb)))    // 遍历到大于或者等于申请大小的chunk
                    victim = victim->bk_nextsize;

                if (victim != last (bin) && victim->size == victim->fd->size)   // 如果这个chunk不是最后一个chunk,并且chunk的大小等于下一个chunk的大小(也就代表有更新的chunk释放和当前chunk大小相同)
                    victim = victim->fd;    // 用最新释放的chunk

                remainder_size = size - nb;   // 计算出分割后剩余大小
                unlink (av, victim, bck, fwd);    // 将chunk解链

                if (remainder_size < MINSIZE) // 如果剩余部分大小小于chunk最低大小
                {
                    set_inuse_bit_at_offset (victim, size);   // 那么整个chunk都分配出去,设置chunk的标志位
                    if (av != &main_arena)
                        victim->size |= NON_MAIN_ARENA;
                }
                else
                {
                    remainder = chunk_at_offset (victim, nb); // 计算出剩余部分chunk的指针
                    bck = unsorted_chunks (av); // 获取unsortedbin指针
                    fwd = bck->fd;  // 获取unsortedbin中第一个chunk
	                if (__glibc_unlikely (fwd->bk != bck))  // 如果unsortedbin第一个chunk的bk不指向unsortedbin则报错
                    {
                      errstr = "malloc(): corrupted unsorted chunks";
                      goto errout;
                    }
                    remainder->bk = bck;    // 将remainder链入unsortedbin
                    remainder->fd = fwd;
                    bck->fd = remainder;
                    fwd->bk = remainder;
                    if (!in_smallbin_range (remainder_size))    // 如果剩余部分属于largebin,那么设置nextsize指针为NULL
                    {
                        remainder->fd_nextsize = NULL;
                        remainder->bk_nextsize = NULL;
                    }
                    set_head (victim, nb | PREV_INUSE | // 设置分配出去的chunk标志
                            (av != &main_arena ? NON_MAIN_ARENA : 0));
                    set_head (remainder, remainder_size | PREV_INUSE);  // 设置剩余部分chunk的标志
                    set_foot (remainder, remainder_size);   // 设置剩余部分chunk的下一个chunk的prev_size为remainder size
                }
                check_malloced_chunk (av, victim, nb);  // DEBUG模式下详细检测
                void *p = chunk2mem (victim);   // chunk转mem
                alloc_perturb (p, bytes);   // 如果设置了pertub_type则初始化内存为pertub_type^0xff
                return p;
            }
        }

本文作者:c3n1g

本文链接:https://www.cnblogs.com/musing/p/18424041

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   c3n1g  阅读(9)  评论(0编辑  收藏  举报
(评论功能已被禁用)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起