一,简介
内存池是一种用于管理和分配内存的技术,它通过预先申请一块固定大小的内存空间,并将其划分为多个小块,提供给程序按需分配和释放。
内存池是池化技术中的一种形式。通常我们在编写程序的时候回使用 new delete 这些关键字来向操作系统申请内存,而这样造成的后果就是每次申请内存和释放内存的时候,都需要和操作系统的系统调用打交道,从堆中分配所需的内存。如果这样的操作太过频繁,就会找成大量的内存碎片进而降低内存的分配性能,甚至出现内存分配失败的情况。 而内存池就是为了解决这个问题而产生的一种技术。从内存分配的概念上看,内存申请无非就是向内存分配方索要一个指针,当向操作系统申请内存时,操作系统需要进行复杂的内存管理调度之后,才能正确的分配出一个相应的指针。而这个分配的过程中,我们还面临着分配失败的风险。 所以,每一次进行内存分配,就会消耗一次分配内存的时间,设这个时间为 T,那么进行 n 次分配总共消耗的时间就是 nT;如果我们一开始就确定好我们可能需要多少内存,那么在最初的时候就分配好这样的一块内存区域,当我们需要内存的时候,直接从这块已经分配好的内存中使用即可,那么总共需要的分配时间仅仅只有 T。当 n 越大时,节约的时间就越多。
二、内存池设计
2.1、定长内存池设计
设计一个内存池,内存池中,分配固定大小的页面和内存块。

首先需要定义管理内存的信息表
typedef struct mempool_s {
int block_size; //总共多少内存块
int free_count; //多少空闲内存块
char *free_ptr; //当前空闲内存块位置
char *mem; //指向内存池起始位置
} mempool_t;
本例中内存块使用指针指向,即前一个内存块指向下一个内存块
下面是具体实现代码
int mp_init(mempool_t *m, int size) {
if (!m) return -1;
if (size < 16) size = 16;
m->block_size = size;
m->mem = (char *)malloc(MEM_PAGE_SIZE);
if (!m->mem) return -1;
m->free_ptr = m->mem;
m->free_count = MEM_PAGE_SIZE / size;
int i = 0;
char *ptr = m->free_ptr;
for (i = 0;i < m->free_count;i++) {
//char是基本字节单位,char*是指针类型,存储的是内存地址
//(char **)ptr告诉编辑器当前位置的内容当作是char*指针处理;*(char **)ptr表示取指向位置的内容赋值为ptr+size
//将下一块内存块的起始地址存储在当前内存块的起始位置
*(char**)ptr = ptr + size;
ptr += size;
}
//相当于最后一个块的next指针为NULL
*(char**)ptr = NULL;
return 0;
}
void mp_dest(mempool_t *m) {
if (!m || !m->mem) return ;
free(m->mem);
}
void *mp_alloc(mempool_t *m) {
if (!m || m->free_count == 0) return NULL;
//ptr指向当前空闲节点
void *ptr = m->free_ptr;
// (char **)ptr中存放的是char*的地址, *(char **)ptr表示取char*
//初始化时已经做好链接,*(char**)ptr存放的是下一个节点的地址,m->free_ptr赋值为*(char**)ptr
m->free_ptr = *(char**)ptr;
m->free_count --;
return ptr;
}
void mp_free(mempool_t *m, void *ptr) {
//ptr表示要删除节点的指针
//*(char**)ptr表示下个节点的地址,这个赋值表示将下个节点的地址改为m->free_ptr;
*(char**)ptr = m->free_ptr;
//m->free_ptr的地址改为ptr
m->free_ptr = (char*)ptr;
m->free_count ++;
}
int main() {
mempool_t m;
mp_init(&m, 32);
void *p1 = mp_alloc(&m);
printf("1: mp_alloc: %p\n", p1);
void *p2 = mp_alloc(&m);
printf("2: mp_alloc: %p\n", p2);
void *p3 = mp_alloc(&m);
printf("3: mp_alloc: %p\n", p3);
void *p4 = mp_alloc(&m);
printf("4: mp_alloc: %p\n", p4);
mp_free(&m, p2);
void *p5 = mp_alloc(&m);
printf("5: mp_alloc: %p\n", p5);
return 0;
}
这个代码的难点在于二级指针的理解,如果不能理解二级指针可以参考博文:【C】二级指针的原理与应用
2.2、变长内存池设计
做一个链表指向空闲内存,分配就是取出一块来,改写链表,返回,释放就是放回到链表里面,并做好归并。注意做好标记和保护,避免二次释放。

设计一个内存池,内存池中,分配固定大小的页面,但是内存块大小是不固定的。
首先需要定义管理内存的信息表
typedef struct mp_node_s {
char *free_ptr;//页面的空闲起始地址
char *end; //页面结束地址
struct mp_node_s *next; //下一个页面
} mp_node_t;
typedef struct mp_pool_s {
struct mp_node_s *first; //第一个页面
struct mp_node_s *current; //当前页面
int max; // 页面大小
} mp_pool_t;
下面是具体实现代码
int mp_init(mp_pool_t *m, int size) {
if (!m) return -1;
void *addr = malloc(size); // 4096字节
mp_node_t *node = (mp_node_t*)addr;
node->free_ptr = (char*)addr + sizeof(mp_node_t);
node->end = (char*)addr + size;
node->next = NULL;
m->first = node;
m->current = node;
m->max = size;
return 0;
}
void mp_dest(mp_pool_t *m) {
if (!m) return ;
while (!m->first) {
void *addr = m->first;
mp_node_t *node = (mp_node_t*)addr;
m->first = node->next;
free(addr);
}
return ;
}
void *mp_alloc(mp_pool_t *m, int size) { //size < (4096-sizeof(mp_node_t))
void *addr = m->current;
mp_node_t *node = (mp_node_t*)addr;
do {
if (size <= (node->end - node->free_ptr)) { //
char *ptr = node->free_ptr;
node->free_ptr += size;
return ptr;
}
node = node->next;
} while (node);
// new node
addr = malloc(m->max); // 4096
node = (mp_node_t*)addr;
node->free_ptr = (char*)addr + sizeof(mp_node_t);
node->end = (char*)addr + m->max;
node->next = m->current;
m->current = node;
char *ptr = node->free_ptr;
node->free_ptr += size;
return ptr;
}
void mp_free(mp_pool_t *m, void *ptr) {
}
int main() {
mp_pool_t m;
mp_init(&m, MEM_PAGE_SIZE);
void *p1 = mp_alloc(&m, 16);
printf("1: mp_alloc: %p\n", p1);
void *p2 = mp_alloc(&m, 32);
printf("2: mp_alloc: %p\n", p2);
void *p3 = mp_alloc(&m, 64);
printf("3: mp_alloc: %p\n", p3);
void *p4 = mp_alloc(&m, 128);
printf("4: mp_alloc: %p\n", p4);
void *p5 = mp_alloc(&m, 256);
printf("5: mp_alloc: %p\n", p5);
mp_dest(&m);
}

396

被折叠的 条评论
为什么被折叠?



