ptmalloc - 第一次申请小内存

ptmalloc - 第一次申请小内存


glibc中malloc的代码包括了对线程同步,平台兼容性等问题的处理,但是本系列文章主要的研究对象ptmalloc。所以提供的代码都是经过简化,部分宏也会展开,能够说清楚ptmalloc的运行流程就可以了。

要点


  • 用例
  • 第一次申请小内存

用例


int main(void)
{
    char   *mem = malloc(10);

    free(mem);

    return  -1;
}

第一次申请小内存


通过gdb调试,我们可以看到,第一个进入的函数是__libc_malloc

void *__libc_malloc(size_t bytes)
{
    mstate ar_ptr;
    void *victim;
    void *(*hook) (size_t, const void *) = __malloc_hook;

    if (hook != NULL) {
        hook(bytes, 0);
    }

    ar_ptr = thread_arena;
    victim = _int_malloc(ar_ptr, bytes);

    return victim;
}

查看__malloc_hook的初始赋值

__malloc_hook = malloc_hook_ini;

继续跟踪malloc_hook_ini

static void *
malloc_hook_ini (size_t sz, const void *caller)
{
    __malloc_hook = NULL;
    ptmalloc_init ();

    return __libc_malloc (sz);
}

static void
ptmalloc_init (void)
{
    if (__malloc_initialized >= 0)
        return;

    __malloc_initialized = 0;
    thread_arena = &main_arena;
}

从malloc_hook_ini 可以看到,最后调用的就是__libc_malloc,往回看,因为hook被设置为NULL,所以接下来调用的就是_int_malloc,先放些宏出来,如果不想看可以直接跳过去看简化的_int_malloc代码

#define SIZE_SZ sizeof(size_t)

/*
 * 如果size_t的大小是8,那么MALLOC_ALIGN_MASK就是8 * 2 -1
 * 2进制是01111111,代码中的作用就是通过位运算将数值16bit对齐
 */
#define MALLOC_ALIGN_MASK (SIZE_SZ * 2 - 1)

/*
 * 最小块大小,每次申请返回的内存都是经过大小调整后加上MIN_CHUNK_SIZE
 * 这样,每次返回的内存前面就包含了两个size_t的数值
 * mchunk_size和mchunk_prev_size
 * x86_64下,sizeof(size_t) = 8,那么MIN_CHUNK_SIZE = 16
 */
#define MIN_CHUNK_SIZE \
    (offsetof(struct malloc_chunk, fd_nextsize))

/*
 * (16 + 15) & ~15 = 16
 */
#define MINSIZE \
    ((MIN_CHUNK_SIZE + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/*
 * 注意代码中的是-2,-2 * MINSIZE然后再强转后就是一个很大的数值了
 */
#define REQUEST_OUT_OF_RANGE(req) \                             
    ((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE))

#define request2size(req) \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
    MINSIZE : \
    /* (req + 8 + 15) & ~15 */
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

#define checked_request2size(req, sz) \
    if (REQUEST_OUT_OF_RANGE (req)) { \
        __set_errno (ENOMEM); \
        return 0; \
    } \
    (sz) = request2size (req);

当size是10的话,参数nb的值就是32,想知道怎么算出来的直接看以上贴出来的宏就可以了

struct malloc_chunk {
    INTERNAL_SIZE_T      mchunk_prev_size;
    INTERNAL_SIZE_T      mchunk_size;

    struct malloc_chunk* fd;
    struct malloc_chunk* bk;

    struct malloc_chunk* fd_nextsize;
    struct malloc_chunk* bk_nextsize;
};

struct mstate {
    ...
    int flags;

    mchunkptr top;
    mchunkptr last_remainder;
    mchunkptr bins[254];
    ...
};

typedef struct malloc_chunk *mbinptr;
typedef struct malloc_state *mstate;

#define bin_at(m, i) \
    (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) - MIN_CHUNK_SIZE 

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
        av->flags |= NONCONTIGUOUS_BIT;

    if (av == &main_arena)
        global_max_fast = 128;

    av->flags |= FASTCHUNKS_BIT;
    av->top = bin_at(av, 1);
}

static void *
_int_malloc (mstate av, size_t bytes)
{
    int32_t nb = 32;

    malloc_init_state(av);

    return sysmalloc(nb, av);
}

代码是很多,不过第一次申请也准备到达尽头了,最后一个函数sysmalloc

static struct malloc_par mp_ =
{
    ...
    .top_pad = 0x20000,
    ...
};

#define PREV_INUSE 0x1

static void *
sysmalloc(size_t nb, mstate av)
{
    size_t pagesize = 4096;
    /*
     * 可以看出第一次申请内存的时候
     * ptmalloc会申请一块很大的内存
     */
    size_t size = nb + mp_.top_pad + MIN_SIZE;

    char *brk;

    /* size必须要pagesize的倍数 */
    size = (size + pagesize - 1) & -pagesize;

    if (size > 0) {
        brk = sbrk(size);
    }

    if (brk != NULL) {
        av->system_mem += size;
    }

    if ((unsigned long) av->system_mem > (unsigned long) (av->max_system_mem))
        av->max_system_mem = av->system_mem;

    size_t remainder_size;
    mchunkptr p = av->top, remainder;

    if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) {
        remainder_size = size - nb;
        remainder = p + nb;
        av->top = remainder;

        /*
         * 要记得nb永远是16的倍数,所以数值的第一字节都是0xX0000
         */
        p->mchunk_size = nb | PREV_INUSE;
        remainder->mchunk_size = remainder_size | PREV_INUSE

        return p + MIN_CHUNK_SIZE;
    }
}

本来的代码有更多的分支,处理,判断,我上面的代码都简化了很多,如果想感受ptmalloc的代码难看性,自行下载吧,下一章有可能是第一次申请大内存或者释放小内存,看心情吧。

完结撒花

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本文通过Glibc的内存暴增问题,主要介绍了系统的内存管理问题,具体如下: 目录 1. 问题 2. 基础知识 2.1 X86平台Linux进程内存布局 2.1.1 32位模式下进程内存经典布局 2.1.2 32位模式下进程默认内存布局 2.1.3 64位模式下进程内存布局 2.2 操作系统内存分配的相关函数 2.2.1 Heap操作相关函数 2.2.2 Mmap映射区域操作相关函数 3. 概述 3.1 内存管理一般性描述 3.1.1 内存管理的方法 3.1.2 内存管理器的设计目标 3.1.3 常见C内存管理程序 3.2 Ptmalloc内存管理概述 3.2.1 简介 3.2.2 内存管理的设计假设 3.2.3 内存管理数据结构概述 3.2.4 内存分配概述 3.2.5 内存回收概述 3.2.6 配置选项概述 3.2.7 使用注意事项 4. 问题分析及解决 5. 源代码分析 5.1 边界标记法 5.2 分箱式内存管理 5.2.1 Small bins 5.2.2 Large bins 5.2.3 Unsorted bin 5.2.4 Fast bins 5.3 核心结构体分析 5.3.1 malloc_state 5.3.2 Malloc_par 5.3.3 分配区的初始化 5.4 配置选项 5.5 Ptmalloc的初始化 5.5.1 Ptmalloc未初始化时分配/释放内存 5.5.2 ptmalloc_init()函数 5.5.3 ptmalloc_lock_all(),ptmalloc_unlock_all(),ptmalloc_unlock_all2() 5.6 多分配区支持 5.6.1 Heap_info 5.6.2 获取分配区 5.6.3 Arena_get2() 5.6.4 _int_new_arena() 5.6.5 New_heap() 5.6.6 get_free_list()和reused_arena() 5.6.7 grow_heap(),shrink_heap(),delete_heap(),heap_trim() 5.7 内存分配malloc 5.7.1 public_mALLOc() 5.7.2 _int_malloc() 5.8 内存释放free 5.8.1 Public_fREe() 5.8.2 _int_free() 5.8.3 sYSTRIm()和munmap_chunk(

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值