genalloc — 通用內存分配器

From: http://www.2cto.com/kf/201302/187782.html


genalloc 是 linux 內核提供的通用內存分配器,源碼位於 lib/genalloc.c。這個分配器爲獨立於內核以外的內存塊提供分配方法,採用的是最先適配原則,android 最新的 ION 內存管理器對 ION_HEAP_TYPE_CARVEOUT 類型的內存就是採用的這個分配器。

1、基礎數據結構
首先看下分配器用到的幾個數據結構,struct gen_pool 用來描述一個內存池:
[cpp]  
struct gen_pool {  
    rwlock_t lock;             /* 鏈表讀寫鎖 */  
    struct list_head chunks;   /* 內存池中內存塊的鏈表 */  
    int min_alloc_order;       /* 內存池最小分配單元的階數,大小爲 2^min_alloc_order */  
};  
在使用的時候需要向內存池中加入內存塊,一個內存塊即一大塊連續的物理內存,用 struct gen_pool_chunk 來描述:
[cpp]  
struct gen_pool_chunk {  
    spinlock_t lock;              /* 操作內存塊時用到的自旋鎖 */  
    struct list_head next_chunk;  /* 加入內存池的節點 */  
    unsigned long start_addr;     /* 內存塊的起始地址 */  
    unsigned long end_addr;       /* 內存塊的結束地址 */  
    unsigned long bits[0];        /* 內存塊的位圖 */  
};  
2、函數接口及調用方法
genalloc 用到的函數接口有下面幾個:
[cpp]  
/* 創建一個內存池,主要工作是完成 struct gen_pool 的初始化 */  
struct gen_pool *gen_pool_create(int min_alloc_order, int nid);  
/* 向內存池中加入內存塊,addr 爲起始地址,size 爲大小 */  
int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, int nid);  
/* 銷燬一個內存池 */  
void gen_pool_destroy(struct gen_pool *pool);  
/* 內存池分配內存的函數 */  
unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size);  
/* 內存池釋放內存的函數 */  
void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size);  
對通用內存分配器的一般使用方法如下:
[cpp]  
/* 初始化內存池,需要創建以及加入內存塊,參數爲:起始地址、大小、最小分配階數 */  
static void *mm_init(uint32_t addr, uint32_t size, uint32_t order)  
{  
    struct gen_pool *pool;  
  
    pool = gen_pool_create(order, 0);  
    if (pool == NULL) {  
        return NULL;  
    }  
  
    if (gen_pool_add(pool, addr, size, 0) != 0) {  
        gen_pool_destroy(pool);  
  
        return NULL;  
    }  
  
    return pool;  
}  
  
/* 銷燬內存池 */  
static void mm_exit(void *handle)  
{  
    gen_pool_destroy(handle);  
}  
  
/* 分配函數 */  
static uint32_t mm_alloc(void *handle, uint32_t size)  
{  
    return gen_pool_alloc(handle, size);  
}  
  
/* 釋放函數 */  
static void mm_free(void *handle, uint32_t addr, uint32_t size)  
{  
    return gen_pool_free(handle, addr, size);  
}  
  
/* 提供給上一級內存管理器調用 */  
struct xxx_mem_ops mm_ops = {  
    .init = mm_init,  
    .exit = mm_exit,  
    .alloc = mm_alloc,  
    .free = mm_free,  
};  
3、分配函數解析
genalloc 通過 gen_pool_alloc 函數來分配內存,下面我們分析一下這個函數的代碼:
[cpp]  
unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)  
{  
    struct list_head *_chunk;  
    struct gen_pool_chunk *chunk;  
    unsigned long addr, flags;  
    int order = pool->min_alloc_order;  
    int nbits, bit, start_bit, end_bit;  
  
    if (size == 0)  
        return 0;  
  
    nbits = (size + (1UL << order) - 1) >> order;  /* 計算申請的內存需要幾個連續的最小單元 */  
  
    read_lock(&pool->lock);  
    list_for_each(_chunk, &pool->chunks) {         /* 遍歷內存池 */  
        chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);  
  
        end_bit = (chunk->end_addr - chunk->start_addr) >> order;        /* 計算當前內存池長度 */  
        end_bit -= nbits + 1;  
  
        spin_lock_irqsave(&chunk->lock, flags);  
        bit = -1;  
        while (bit + 1 < end_bit) {  /* 循環查找最先適配的內存區 */  
            bit = find_next_zero_bit(chunk->bits, end_bit, bit + 1);     /* 尋找爲0的bit */  
            if (bit >= end_bit)      /* 循環結束 */  
                break;  
  
            start_bit = bit;         /* 起始位置 */  
            if (nbits > 1) {         /* 如果申請的內存大於一個最小單元,查找連續的nbits個單元 */  
                bit = find_next_bit(chunk->bits, bit + nbits,bit + 1);  
                if (bit - start_bit < nbits)  
                    continue;  
            }  
  
            addr = chunk->start_addr + ((unsigned long)start_bit << order);  /* 計算申請的內存的起始地址 */  
            while (nbits--)  
                __set_bit(start_bit++, chunk->bits);  /* 將申請到的單元全部標記爲已用 */  
            spin_unlock_irqrestore(&chunk->lock, flags);  
            read_unlock(&pool->lock);  
            return addr;  
        }  
        spin_unlock_irqrestore(&chunk->lock, flags);  
    }  
    read_unlock(&pool->lock);  
    return 0;  
}  
因爲是用的最先適配原則,所以邏輯比較簡單,我們也可以根據自己的需求實現最適合分配器以及夥伴分配器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章