ptmalloc

来源:互联网 发布:哪里有网络水军可以请 编辑:程序博客网 时间:2024/05/16 04:52

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 voidptmalloc_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 0x1static 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的代码难看性,自行下载吧,下一章有可能是第一次申请大内存或者释放小内存,看心情吧。

完结撒花