genalloc — 通用内存分配器
来源:互联网 发布:java写界面的工具 编辑:程序博客网 时间:2024/06/18 00:20
genalloc 是 linux 内核提供的通用内存分配器,源码位于 lib/genalloc.c。这个分配器为独立于内核以外的内存块提供分配方法,采用的是最先适配原则,android 最新的ION 内存管理器对ION_HEAP_TYPE_CARVEOUT 类型的内存就是采用的这个分配器。
1、基础数据结构
首先看下分配器用到的几个数据结构,struct gen_pool 用来描述一个内存池:
struct gen_pool {rwlock_t lock; /* 链表读写锁 */struct list_head chunks; /* 内存池中内存块的链表 */int min_alloc_order; /* 内存池最小分配单元的阶数,大小为 2^min_alloc_order */};在使用的时候需要向内存池中加入内存块,一个内存块即一大块连续的物理内存,用 struct gen_pool_chunk 来描述:
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 用到的函数接口有下面几个:
/* 创建一个内存池,主要工作是完成 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);对通用内存分配器的一般使用方法如下:
/* 初始化内存池,需要创建以及加入内存块,参数为:起始地址、大小、最小分配阶数 */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 函数来分配内存,下面我们分析一下这个函数的代码:
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;}
因为是用的最先适配原则,所以逻辑比较简单,我们也可以根据自己的需求实现最适合分配器以及伙伴分配器。
附1、buddy 分配器
平台实现了一个 buddy 分配器,代码很精致:
struct buddy_unit {uint8_tused:1;/* 1 if allocated, 0 if free */uint8_torder:7;/* size of the region in buddy space */};struct buddy_pool {uint32_taddr;/* the start addr of the buddy area */uint32_tsize;/* the total size of the buddy area */uint32_torder;/* the base order of each unit */uint32_ttotal;/* total units */uint32_tfree;/* unused units */struct buddy_unit*bitmap;/* the bitmap of all buddy units */spinlock_tlock;};struct buddy_pool *buddy_create(uint32_t addr, uint32_t size, uint32_t order){struct buddy_pool *pool; /* buddy内存池 */uint32_t end;int index, uorder;/* 以 2^order = 2^15 = 32KB 为管理单元修正参数 */end = (addr + size) & ~((1 << order) - 1); /* buddy区结束地址 */addr = addr & ~((1 << order) - 1); /* buddy区起始地址 */if (end <= addr) {return NULL;}/* 实例化buddy内存池 */pool = kmalloc(sizeof(struct buddy_pool), GFP_KERNEL);if (pool == NULL) {return NULL;}pool->total = size >> order; /* buddy区以32KB为单元的内存块总数 *//* 申请管理位图,每个单元占用一个字节 */pool->bitmap = kzalloc(sizeof(struct buddy_unit) * pool->total, GFP_KERNEL);if (pool->bitmap == NULL) {kfree(pool);return NULL;}/* 初始化各参数 */pool->free = pool->total;pool->addr = addr;pool->size = size;pool->order = order;spin_lock_init(&pool->lock);mdbg("create buddy: total=%d, order=%d\n", pool->total, pool->order);/* initialize the bitmap orders */index = 0;uorder = BUDDY_MAX_ORDER; /* buddy区最大的分配阶数 */while (index < pool->total) {while (index + (1 << uorder) > pool->total) {uorder--; /* e.g: 2^24 = 16MB, total = 2^9, uorder = 9 */}pool->bitmap[index].order = uorder; /* 只初始化buddy的第一个成员 */index += 1 << uorder;}return pool;}uint32_t buddy_alloc(struct buddy_pool *pool, uint32_t size){int i, order, buddy, index = -1;/* 获取申请内存长度的order */for (i = 0; i <= BUDDY_MAX_ORDER; i++) {if ((1 << (pool->order + i)) >= size) {break;}}if (i > BUDDY_MAX_ORDER) {minfo("size is too big: 0x%08x\n", size);return 0;}order = i;spin_lock(&pool->lock);i = 0;do { /* 扫描管理位图 */mdbg("index = %d\n", index);/* 判断当前内存单元是否已经被申请 */if (pool->bitmap[i].used == 0) { /* 如果没有被申请 */if (order == pool->bitmap[i].order) { /* 如果当前order与申请的order相同则跳出循环 *//* matched order */index = i; /* 将编号赋给index */break;} else if (order < pool->bitmap[i].order) { /* 如果申请order小于当前order *//* get the best fit buddy unit */if (index == -1) { /* 如果是第一次匹配 */index = i;} else if (pool->bitmap[i].order < pool->bitmap[index].order) {/* 当前order小于上次匹配的order */index = i; /* 则将匹配order更新为当前值,这样可以保证是用的最小的buddy来分裂 */}}}i += 1 << pool->bitmap[i].order; /* 移动到下一个可以分配单元 */} while (i < pool->total);/* no free buddy unit found */if (index == -1) {spin_unlock(&pool->lock);minfo("no buddy found!\n");return 0;}/* 如果申请order小于找到的order,则将找到的buddy分裂,直到相等 */while (order < pool->bitmap[index].order) {pool->bitmap[index].order--; /* 将当前buddy的order减一 *//* Buddy# = Slot# ^ (1 << order) */buddy = index ^ (1 << pool->bitmap[index].order); /* 找到分裂出来的buddy */pool->bitmap[buddy].order = pool->bitmap[index].order; /* 将分裂出来的buddy的order也减一 */}pool->bitmap[index].used = 1; /* 将找到的buddy标记为已用 */pool->bitmap[index].order = order; /* 重复赋值? */pool->free -= (1 << order); /* 总的空闲单元数减去申请成功的单元数 */spin_unlock(&pool->lock);return pool->addr + (index << pool->order); /* 返回申请到内存块的起始物理地址 */}
- genalloc — 通用内存分配器
- genalloc — 通用内存分配器
- genalloc — 通用内存分配器
- genalloc — 通用内存分配器
- genalloc — 通用内存分配器
- genalloc — 通用内存分配器
- tcmalloc——内存分配器
- 内存分配器
- 内存分配器
- 内存分配器
- 简单内存分配器
- ACE内存分配器一
- ACE内存分配器二
- ACE内存分配器三
- STL的内存分配器
- STL中的内存分配器
- 快速的内存分配器
- GFP-Tree 内存分配器
- 指定数据库存储路径
- Oracle 用户、对象权限、系统权限
- 流
- (HLS播放器之一)HLS协议之M3U8解析
- 使用jadclipse+jad 在Eclipse中反编译Class文件详解
- genalloc — 通用内存分配器
- 调用系统设置、管理应用程序、壁纸程序
- js 事件模型中不同浏览器的监听和解除监听方式参考
- 玩转 Android MediaPlayer之视频预加载
- DuckDuckGo
- disable maven autobuild
- 让sql语句结果集不排序,按照in语句的顺序返回结果
- 玩转 Android MediaPlayer之Media Proxy
- org.jbpm.api.JbpmException: No unnamed transitions were found for the task '?????'