zoukankan      html  css  js  c++  java
  • C 工具库6:通用内存分配器

    本篇将介绍一个通用内存分配器的实现.
    上一篇的开头已经介绍过,这个实现是组合first fit pool和fix_obj_pool以处理不同大小的分配请求.


    首先看由fix_obj_pool返回的内存的头结构:

    struct head_fix
    {
    unsigned short idx;/*0-255,表示在fix_objs的下标*/
    unsigned short chunk_idx;
    };

    这个头不会返回给上层应用,是给释放内存使用的.因为内存的请求是对齐到4字节的,小于1024字节的请求,被分成256个分配大小,分别是4,
    8,12,....1024.每个大小的请求由一个fix_obj_pool处理.idx的作用就是记录是由哪个fix_obj_pool分配的内存.还有一点要注意的是,head_fix
    本身占用了4个字节,所以fix_obj_pool分配的对象大小实际是请求大小对齐到4倍数的大小再加上4.例如如果分配1个字节,对齐到4就是4字节,
    所以实际分配内存的大小是8,只有后面4个字节会返回给上层应用.


    这种空间的浪费对于小的内存请求有点过分了,所以在实际使用中,这个所谓的通用内存分配器,其实不是为编译时就已经可以确定大小的对象
    使用的,对于编译期就确定大小的对象,应该直接使用fix_obj_pool.只有那些在运行时才确定大小的内存分配请求,例如字符串,变长结构等
    才应该使用通用内存分配器.


    下面介绍chunk_idx的作用.
    首先回顾fix_obj_pool的实现,一个chunk中的所有对象在地址上都是连续的,当chunk的空间不够时,会再分配一个chunk,然后将这些chunk按期首地
    址大小的升序排序.这样,释放一个对象时,可以根据对象地址使用2分查找快速的找到对象归属的chunk.
    虽然2分查找效率是比较高的,但最好还是能避免.在单独使用fix_obj_pool的情况小,一般可以大致估算到系统中最大可能会分配多少个这类对象.
    在创建fix_obj_pool时,传入这个估算值,就可以生成一个大小基本符合要求的chunk,这样几乎在大多数情况下都避免了释放内存时的一次2分查找.


    但对于通用内存分配器,我们不太好估计这个值,所以,chunk的初始大小一般都不会设得太大.为了避免二分查找,在分配分时,通过调用:
    void *pool_alloc2(struct fix_obj_pool *pool,unsigned short *chunk_idx)
    把chunk的下标一并返回,释放时直接把这个下标传进去就可以定位到正确的chunk了.


    下面是通用内存分配器的代码:

    mem_allocator.h

    #ifndef _MEM_ALLOCATOR_H
    #define _MEM_ALLOCATOR_H

    struct allocator;

    /*
    * max:可以分配的最大请求字节数
    */
    struct allocator *allocator_create(unsigned int max);
    void allocator_destroy(struct allocator **);

    void *allocator_alloc(struct allocator*,int);
    void allocator_dealloc(struct allocator*,void*);

    #endif

    mem_allocator.c

    #include "mem_allocator.h"
    #include "../first_fit/first_fit.h"
    #include "../first_fit/first_fit_define.h"
    #include "../fix_obj_pool/fix_obj_pool.h"
    #include "http://www.cnblogs.com/vector/vector.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <string.h>


    #define FIRST_FIT_DEFAULT_SIZE 1024*1024*16 //默认first_fit分配器大小是16MB

    /*
    * fix_obj_pool用于分配从1-1024字节大小的请求
    * 请求对齐到4字节.所以总共有1024/4 = 256种大小
    */

    struct allocator
    {
    unsigned int max_request; //可以处理的最大分配请求
    struct fix_obj_pool *fix_objs[1024/4];
    struct vector *first_fits;//存放first_fit
    int last_dealloc_idx;
    };

    struct head_fix
    {
    unsigned short idx;/*0-255,表示在fix_objs的下标*/
    unsigned short chunk_idx;
    };

    struct head_first_fit
    {
    unsigned int idx;//在first_fits中的下标
    };

    struct chunk;
    extern void *pool_alloc2(struct fix_obj_pool*,unsigned short *chunk_idx);
    extern void pool_dealloc2(struct fix_obj_pool *pool,unsigned short chunk_idx,void* obj);

    struct allocator *allocator_create(unsigned int max)
    {
    struct allocator *al = malloc(sizeof(*al));
    if(!al)
    return 0;
    memset(al,sizeof(*al),0);
    al->max_request = max;
    al->last_dealloc_idx = -1;
    al->first_fits = 0;
    int i = 0;
    for(; i < 1024/4; ++i)
    {
    unsigned int real_size = (i+1)*4 + sizeof(struct head_fix);
    al->fix_objs[i] = create_pool(real_size,1024,1);
    if(!al->fix_objs[i])
    {
    allocator_destroy(&al);
    return 0;
    }
    }

    al->first_fits = vector_create(sizeof(struct first_fit_pool*),0);
    struct first_fit_pool *first_fit = first_fit_create(FIRST_FIT_DEFAULT_SIZE);
    if(!first_fit)
    {
    allocator_destroy(&al);
    return 0;
    }
    VECTOR_PUSH_BACK(struct first_fit_pool*,al->first_fits,first_fit);
    return al;
    }

    void allocator_destroy(struct allocator **al)
    {
    assert(al);
    assert(*al);
    unsigned int i = 0;
    if((*al)->first_fits)
    {
    unsigned int size = vector_size((*al)->first_fits);
    struct first_fit_pool **first_fits = VECTOR_TO_ARRAY(struct first_fit_pool *,(*al)->first_fits);
    for( ; i < size; ++i)
    first_fit_destroy(&first_fits[i]);
    }

    i = 0;
    for( ; i < 1024/4;++i)
    {
    if((*al)->fix_objs[i])
    destroy_pool(&(*al)->fix_objs[i]);
    }

    free(*al);
    *al = 0;
    }

    void *allocator_alloc(struct allocator *al,int size)
    {
    assert(al);
    unsigned int alloc_size = size;
    if(size <= 0)
    alloc_size = 1;
    //将请求大小调整为4的倍数
    alloc_size = alignsize(alloc_size);
    if(alloc_size > al->max_request)
    return 0;//无法处理这个请求
    if(alloc_size > 1024)
    {
    struct head_first_fit *hff = 0;
    //交给first fit分配器
    alloc_size += sizeof(struct head_first_fit);
    if(al->last_dealloc_idx >= 0)
    {
    //首先尝试从上次释放的first_fit分配
    struct first_fit_pool *ff = VECTOR_GET(struct first_fit_pool *,al->first_fits,al->last_dealloc_idx);
    assert(ff);
    hff = (struct head_first_fit*)first_fit_alloc(ff,alloc_size);
    }

    if(!hff)
    {
    //遍历first_fits
    struct first_fit_pool **ffs = VECTOR_TO_ARRAY(struct first_fit_pool *,al->first_fits);
    unsigned int i = 0;
    unsigned int end = vector_size(al->first_fits);
    for( ; i < end; ++i)
    {
    hff = first_fit_alloc(ffs[i],alloc_size);
    if(hff)
    break;
    }
    if(!hff)
    {
    //分配一个新的first_fit;
    struct first_fit_pool *ff = first_fit_create(FIRST_FIT_DEFAULT_SIZE);
    if(!ff)
    return 0;
    VECTOR_PUSH_BACK(struct first_fit_pool*,al->first_fits,ff);
    hff = first_fit_alloc(ff,alloc_size);
    }

    void *ret = (void*)((char*)hff + sizeof(*hff));
    return ret;
    }
    }
    else
    {
    //由fix_obj_pool处理
    unsigned char idx = alloc_size/4 - 1;
    unsigned short chunk_idx;
    struct head_fix *hf = pool_alloc2(al->fix_objs[idx],&chunk_idx);
    if(!hf)
    return 0;
    hf->idx = idx;
    hf->chunk_idx = chunk_idx;
    void *ret = (void*)((char*)hf + sizeof(*hf));
    return ret;
    }
    }

    void allocator_dealloc(struct allocator *al,void *ptr)
    {
    assert(al);
    assert(ptr);
    struct first_fit_chunk dummy;
    //首先确定是哪种类型的分配器分配的
    struct first_fit_chunk *ffc = (struct first_fit_chunk *)((int*)ptr - sizeof(struct head_first_fit)/4 - sizeof(dummy.tag)/4 - sizeof(dummy.size)/4);
    if(ffc->tag == USED_TAG)
    {
    //由first_fit分配的
    struct head_first_fit *hff = (struct head_first_fit *)((int*)ptr - sizeof(struct head_first_fit)/4);
    if(hff->idx < vector_size(al->first_fits))
    {
    struct first_fit_pool *ff = VECTOR_GET(struct first_fit_pool *,al->first_fits,hff->idx);
    assert(ff);
    first_fit_dealloc(ff,hff);
    al->last_dealloc_idx = hff->idx;
    }
    }
    else
    {
    struct head_fix *hf = (struct head_fix *)((int*)ptr - sizeof(struct head_fix)/4);
    if(hf->idx <= 255)
    {
    pool_dealloc2(al->fix_objs[hf->idx],hf->chunk_idx,ptr);
    }
    }
    }






  • 相关阅读:
    高德地图iOS SDK限制地图的缩放比例
    c++如何遍历删除map/vector里面的元素
    cocos2d-x与UIKit混合编程实现半透明效果
    cocos2d-x中的坑
    [redis]-redis查看key的大小(方法一在centos亲测通过)
    openssl 从cer文件中提取公钥
    SSL证书去除rsa私钥密码保护(.pem)
    RHEL8 CentOS8 下安装 MySQL 8.0<亲测>
    centos7+nginx使用Certbot让网站拥有https
    idea将javaweb项目部署到tomcat
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2429617.html
Copyright © 2011-2022 走看看