【C】一文速学----内存池原理与实战

一,简介

内存池是一种用于管理和分配内存的技术,它通过预先申请一块固定大小的内存空间,并将其划分为多个小块,提供给程序按需分配和释放。

内存池是池化技术中的一种形式。通常我们在编写程序的时候回使用 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);

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值