boot中的malloc实现分析

初始化内存堆部分:
cfe_main()
{
    /*
     * Initialize the memory allocator
     */
    KMEMINIT((unsigned char *) (uintptr_t) mem_heapstart,
         ((CFG_HEAP_SIZE)*1024));
}

#define KMEMINIT(buffer,length) kmeminit(&kmempool,(buffer),(length))

mempool_t kmempool;            /* default pool */                         //编译期分配的内存空间

struct mempool_s {
    memnode_t *root;            /* pointer to root node */
    unsigned char *base;        /* base of memory region */
    unsigned int length;        /* size of memory region */
};

typedef enum { memnode_free = 0, memnode_alloc } memnode_status_t;

typedef struct memnode_s {
    unsigned int seal;
    struct memnode_s *next;        /* pointer to next node */
    unsigned int length;        /* length of the entire data section */
    memnode_status_t status;        /* alloc/free status */
    unsigned char *data;        /* points to actual user data */
    void *memnodeptr;            /* memnode back pointer (see comments) */
} memnode_t;

void kmeminit(mempool_t *pool,unsigned char *buffer,int length)
{
    pool->root = (memnode_t *) buffer;   //在空块内存的头部创建节点
    pool->root->seal = MEMNODE_SEAL;     //魔数    #define MEMNODE_SEAL 0xFAAFA123        /* just some random constant */
    pool->root->length = length - sizeof(memnode_t);     //减掉头部剩下的内存大小
    pool->root->data = memnode_data(unsigned char *,pool->root);   //指针偏移+头部的大小
    pool->root->status = memnode_free;     //标记内存块为free状态
    pool->root->next = NULL;                       //指向下一个内存块
    pool->base = buffer;       //内存池的基地址
    pool->length = length;   //内存池的长度
}

#define memnode_data(t,m) (t) (((memnode_t *) (m))+1)

分配内存使用 KMALLOC:

#define KMALLOC(size,align) kmalloc(&kmempool,(size),(align))

/*  *********************************************************************
    *  kmalloc(pool,size,align)
    *  
    *  Allocate some memory from the pool.  
    *  
    *  Input parameters:
    *      pool - pool structure
    *         size - size of item to allocate
    *         align - alignment (must be zero or a power of 2)
    *         
    *  Return value:
    *         pointer to data, or NULL if no memory left
    ********************************************************************* */
void *kmalloc(mempool_t *pool,unsigned int size,unsigned int align)
{
    memnode_t *m;
    memnode_t *newm;
    memnode_t **backptr;
    uintptr_t daddr = 0;
    uintptr_t realsize = 0;
    uintptr_t extra;
    uintptr_t blkend;
    uintptr_t ptralign;
    /*
     * Everything should be aligned by at least the
     * size of an int64
     */
    ptralign = (uintptr_t) align;
    if (ptralign < sizeof(void *)) ptralign = sizeof(uint64_t);     //最小对齐单元为 int64
    /*    
     * Everything should be at least a multiple of the
     * size of a pointer.
     */
    if (size == 0) size = sizeof(void *);   //分配内存大size必须要是4字节的整数倍
    if (size & (sizeof(void *)-1)) {
    size += sizeof(void *);
    size &= ~(sizeof(void *)-1);
    }
    /*
     * Find a memnode at least big enough to hold the storage we
     * want.
     */
    for (m = pool->root; m; m = m->next) {
    if (m->status == memnode_alloc) continue;
    /*
     * If we wanted a particular alignment, we will
     * need to adjust the size.
     */
    daddr = memnode_data(uintptr_t,m);
    extra = 0;
    if (daddr & (ptralign-1)) {
        extra = ptralign - (daddr & (ptralign-1));
        }
    realsize = size + extra;             //加上对齐需要浪费的内存
    if (m->length < realsize) continue;
    break;
    }
    /*
     * If m is null, there's no memory left.
     */
    if (m == NULL) {
    lib_outofmemory();
    return NULL;
    }
    /*
     * Otherwise, use this block.  Calculate the address of the data
     * to preserve the alignment.
     */
    if (daddr & (ptralign-1)) {
    daddr += ptralign;
    daddr &= ~(ptralign-1);
    }
    /* Mark this node as allocated. */
    m->data   = (unsigned char *) daddr;         //存放对齐后分配的数据的起始地址
    m->status = memnode_alloc;
    /*
     * Okay, this is ugly.  Store a pointer to the original
     * memnode just before what we've allocated.  It's guaranteed
     * to be aligned at least well enough for this pointer.
     * If for some reason the memnode was already exactly
     * aligned, backing up will put us inside the memnode
     * structure itself... that's why the memnodeptr field
     * is there, as a placeholder for this eventuality .
     */
    backptr   = (memnode_t **) (m->data - sizeof(memnode_t *));             //将头结点的位置存放在分配的内存的头四个字节中
    *backptr  = m;
    /*
     * See if we need to split it.
     * Don't bother to split if the resulting size will be
     * less than MINBLKSIZE bytes
     */
    if (m->length - realsize < MINBLKSIZE) {             // #define MINBLKSIZE 64        如果剩余的内存小于64个字节,则直接不分割当前内存块,因为头结点还要占据一些字节
    return m->data;
    }
    /*
     * Split this block.  Align the address on a pointer-size
     * boundary.
     */
    daddr += size;
    if (daddr & (uintptr_t)(sizeof(void *)-1)) {                 //4字节对齐
    daddr += (uintptr_t)sizeof(void *);
    daddr &= ~(uintptr_t)(sizeof(void *)-1);
    }
    blkend = memnode_data(uintptr_t,m) + (uintptr_t)(m->length);           //当前内存块的结束地址
    newm = (memnode_t *) daddr;
    newm->next   = m->next;
    m->length    = (unsigned int) (daddr - memnode_data(uintptr_t,m));     //除去头部后内存块的长度,包括未对齐的内存
    m->next      = newm;
    m->status    = memnode_alloc;
    newm->seal   = MEMNODE_SEAL;
    newm->data    = memnode_data(unsigned char *,newm);
    newm->length = (unsigned int) (blkend - memnode_data(uintptr_t,newm));
    newm->status = memnode_free;           //标记剩余的内存卡为free状态
    return m->data;    
}

通过上面的分析可以看出,其实data这个字段可以去掉,因为它的左右不大,对应free的内存块,它指向的是头结点后紧跟在的内存地址,对于分配了的内存块来说,它存放的是头结点后对齐后的内存地址,也即分配返回的地址,而这个可以通过前面紧跟着的4个字节获取头结点的位置,所以说data字段是可以去掉的。
length字段始终存放的是内存块去掉头结点后的大小,包括所有未对齐的内存

内存块链表是按照内存地址顺序连接的,这样做的目的是为了方便内存合并,见下文解释。

释放内存使用 KFREE:
#define KFREE(ptr) kfree(&kmempool,(ptr))

/*  *********************************************************************
    *  kfree(ptr)
    *  
    *  Return some memory to the pool.
    *  
    *  Input parameters:
    *         ptr - pointer to something allocated via kmalloc()
    *         
    *  Return value:
    *         nothing
    ********************************************************************* */
void kfree(mempool_t *pool,void *ptr)
{
    memnode_t **backptr;
    memnode_t *m;
    if (((unsigned char *) ptr < pool->base) ||                       //检查要释放的地址是否在内存池地址范围内
    ((unsigned char *) ptr >= (pool->base+pool->length))) {
#ifdef TESTPROG
    printf("Pointer %08X does not belong to pool %08X\n",ptr,pool);
#endif
    return;
    }
    backptr = (memnode_t **) (((unsigned char *) ptr) - sizeof(memnode_t *));
    m = *backptr;                //获取头结点的位置
    if (m->seal != MEMNODE_SEAL) {       //判断头的魔数
#ifdef TESTPROG
    printf("Invalid node freed: %08X\n",m);
#endif
    return;
    }
    m->status = memnode_free;           //标记为free状态
    kmemcompact(pool);               //进行内存紧缩,即合并临近的空闲内存块,防止内存碎片   
}


/*  *********************************************************************
    *  kmemcompact(pool)
    *  
    *  Compact the memory blocks, coalescing consectutive free blocks
    *  on the list.
    *  
    *  Input parameters:
    *         pool - pool descriptor
    *         
    *  Return value:
    *         nothing
    ********************************************************************* */
static void kmemcompact(mempool_t *pool)
{
    memnode_t *m;
    int compacted;
    do {
    compacted = 0;
    for (m = pool->root; m; m = m->next) {               //遍历内存池中的所有内存块
        /* Check seal to be sure that we're doing ok */
        if (m->seal != MEMNODE_SEAL) {
#ifdef TESTPROG
        printf("Memory list corrupted!\n");
#endif
        return;
        }
        /*
         * If we're not on the last block and both this
         * block and the next one are free, combine them
         */
        if (m->next &&
        (m->status == memnode_free) &&
        (m->next->status == memnode_free)) {             //连续两块内存为free状态
        m->length += sizeof(memnode_t) + m->next->length;
        m->next->seal = 0;
        m->next = m->next->next;
        compacted++;
        }
        /* Keep going till we make a pass without doing anything. */
        }
    } while (compacted > 0);     //当有内存卡合并发生时,重新遍历一遍
}

通过以上的代码可以看出该malloc实现算法非常简单,缺点是直接找到第一块大内存进行分割,这样很容易产生碎片。

对应的内存分配测试程序如下所示:
#ifdef TESTPROG
int kmemchk(mempool_t *pool,int verbose)
{
    memnode_t *m;
    memnode_t **backptr;
    unsigned int daddr;
    for (m = pool->root; m; m = m->next) {
    if (verbose) {
        printf("%08X: Next=%08X  Len=%5u  %s  Data=%08X ",
           m,m->next,m->length,
           m->status ? "alloc" : "free ",
           m->data);
        }
    daddr = memnode_data(uintptr_t,m);
    if (m->seal != MEMNODE_SEAL) {
        if (verbose) printf("BadSeal ");
        else return -1;
        }
    if (m->next && (daddr + m->length != (unsigned int) m->next)) {
        if (verbose) printf("BadLength ");
        else return -1;
        }
    if (m->next && (m->next < m)) {
        if (verbose) printf("BadOrder ");
        else return -1;
        }
    if (m->data < (unsigned char *) m) {
        if (verbose) printf("BadData ");
        else return -1;
        }
    if (m->status == memnode_alloc) {
        backptr = (memnode_t **) (m->data - sizeof(void *));
        if (*backptr != m) {
        if (verbose) printf("BadBackPtr ");
        else return -1;
        }
        }
    if (verbose) printf("\n");
    }
    return 0;
}
#define MEMSIZE 1024*1024
unsigned char *ptrs[4096];
unsigned int sizes[4096];
/*  *********************************************************************
    *  main(argc,argv)
    *  
    *  Test program for the memory allocator
    *  
    *  Input parameters:
    *         argc,argv
    *         
    *  Return value:
    *         nothing
    ********************************************************************* */
void main(int argc,char *argv[])
{
    unsigned char *mem;
    int items = 0;
    int idx;
    int size;
    int totalsize = 0;
    int nfree,freecnt;
    mempool_t *pool = &kmempool;
    mem = malloc(MEMSIZE);
    kmeminit(pool,mem,MEMSIZE);
    items = 0;
    for (;;) {
    for (;;) {
        if (items == 4096) break;
        size = rand() % 1024;
        ptrs[items] = kmalloc(pool,size,1<<(rand() & 7));
        if (!ptrs[items]) break;
        sizes[items] = size;
        items++;
        totalsize += size;
        }
    printf("%d items allocated, %d total bytes\n",items,totalsize);
    if (kmemchk(pool,0) < 0) {
        kmemchk(pool,1);
        exit(1);
        }
    /* Scramble the pointers */
    idx = items - 1;
    while (idx) {
        if (rand() & 2) {
        mem = ptrs[0];
        ptrs[0] = ptrs[idx];
        ptrs[idx] = mem;
        nfree = sizes[0];
        sizes[0] = sizes[idx];
        sizes[idx] = nfree;
        }
        idx--;
        }
    
    /* now free a random number of elements */
    nfree = rand() % items;
    freecnt = 0;
    for (idx = nfree; idx < items; idx++) {
        kfree(pool,ptrs[idx]);
        totalsize -= sizes[idx];
        freecnt++;
        ptrs[idx] = NULL;
        sizes[idx] = 0;
        if (kmemchk(pool,0) < 0) {
        kmemchk(pool,1);
        exit(1);
        }
        }
    items -= freecnt;
    printf(".");
    }
    kmemchk(pool,1);
    exit(0);
}
#endif     /* TESTPROG */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值