内存管理
内存分配
解决问题
- 版本迭代代码修改容易造成内存泄漏。
- 每次都要进行频繁的堆数据分配导致碎片和效率低下。
- 处理小块分配,进行统一大块的管理内存。
需要的功能
- 分配
- 回收
- 扩容
分配和回收制度用到伙伴算法,它是一种为了核心内存管理能够快速响应请求,尽可能地在提高内存利用率的同时减少内存碎片的一种算法。
伙伴系统
伙伴系统算法把内存中的所有页框按照大小分成10组不同大小的页块,每块分别包含1,2,4,8,……,512个页框。在Linux系统中每种不同的页块都通过一个free-area-struct结构体来管理。系统将10个free-area-struct结构体组成一个free-area[]数组。在free-area-struct包含指向空闲页块链表的指针。此外在每个free-area-struct中还包含一个系统空闲页块位图(bitmap),位图中的每一位都用来表示系统按照当前页块大小划分时每个页块的使用情况,同mem-map一样,系统在初始化时调用free-area-struct() -area-struct中的位图结构。
优点:
较好的解决外部碎片问题
针对大内存来进行分配
只要请求的块不超过512页面,内核就分配连续的页面
缺点:
合并要求太过严格,符合伙伴关系的块才可以合并
减少外部碎片的代价是以产生内部碎片为代价
内存块的一个页面被占用,导致整块的内存区都不具备合并的条件
Slab
分配内核内存的第二种策略称为slab分配。每个 slab 由一个或多个物理连续的页面组成,每个 cache 由一个或多个 slab 组成,每个内核数据结构都有一个 cache。
例如,用于表示进程描述符、文件对象、信号量等的数据结构都有各自单独的 cache。每个 cache 含有内核数据结构的对象实例(称为 object)。例如,信号量 cache 有信号量对象,进程描述符 cache 有进程描述符对象,等等
内存池
在用户态设计一个内存池,实现自动管理内存,主要分为两个部分,一个是小块内存的分配,一个是大块内存的分配,小块内存mp_node_s,大块内存mp_large_s。根据分配的数据大小来定义
结构体的定义mp_node_s
struct mp_node_s { // 针对4K以下的
unsigned char *end; // 结束
unsigned char * last; // 结尾
struct mp_node_s *next; // 指向下一个结构体将它们链起来。
};
struct mp_large_s { // 针对4K以上的
struct mp_large_s *next; // 下一个
void *alloc; // 具体内容地址
}
struct mp_pool_s { // 管理两部分内存的总结构体
struct mp_large_s *large;
struct mp_node_s *head;
struct mp_node_s current;
}
创建
struct mp_pool_s *mp_create_pool(size_t size) {
struct mp_pool_s *p;
int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s));
if (ret) {
return NULL;
}
p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
p->current = p->head;
p->large = NULL;
p->head->last = (unsigned char *)p + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
p->head->end = p->head->last + size;
p->head->failed = 0;
return p;
}
分配
内存的分配分为两部分一部分是大块,一部分是小块。
小块
void *mp_alloc(struct mp_pool_s *pool, size_t size) {
unsigned char *m;
struct mp_node_s *p;
if (size <= pool->max) {
p = pool->current;
do {
m = mp_align_ptr(p->last, MP_ALIGNMENT);
if ((size_t)(p->end - m) >= size) {
p->last = m + size;
return m;
}
p = p->next;
} while (p);
return mp_alloc_block(pool, size);
}
return mp_alloc_large(pool, size);
}
static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {
unsigned char *m;
struct mp_node_s *h = pool->head;
size_t psize = (size_t)(h->end - (unsigned char *)h);
int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize);
if (ret) return NULL;
struct mp_node_s *p, *new_node, *current;
new_node = (struct mp_node_s*)m;
new_node->end = m + psize;
new_node->next = NULL;
new_node->failed = 0;
m += sizeof(struct mp_node_s);
m = mp_align_ptr(m, MP_ALIGNMENT);
new_node->last = m + size;
current = pool->current;
for (p = current; p->next; p = p->next) {
if (p->failed++ > 4) {
current = p->next;
}
}
p->next = new_node;
pool->current = current ? current : new_node;
return m;
}
大块
static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {
void *p = malloc(size);
if (p == NULL) return NULL;
size_t n = 0;
struct mp_large_s *large;
for (large = pool->large; large; large = large->next) {
if (large->alloc == NULL) {
large->alloc = p;
return p;
}
if (n ++ > 3) break;
}
large = mp_alloc(pool, sizeof(struct mp_large_s));
if (large == NULL) {
free(p);
return NULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
释放
void mp_destory_pool(struct mp_pool_s *pool) {
struct mp_node_s *h, *n;
struct mp_large_s *l;
for (l = pool->large; l; l = l->next) {
if (l->alloc) {
free(l->alloc);
}
}
h = pool->head->next;
while (h) {
n = h->next;
free(h);
h = n;
}
free(pool);
}