内存池的设计

参考了lwip和btstack等开源协议栈的内存池的设计,我来分析一下内存池的设计:

下面是参考的(博主:老衲五木)对lwip动态内存池的理解,这对我理解btstack内存池的分配的理解帮助很大

目录

参考设计

       动态内存池(POOL)分配策略可以说是一个比较笨的分配策略了,但其分配策略实现简单,内存的分配、释放效率高,可以有效防止内存碎片的产生。不过,他的缺点是会浪费部分内存。

       为什么叫 POOL?这点很有趣,POOL 有很多种,而这点依赖于用户配置 LWIP 的方式。例如用户在头文件 opt.h 文件中定义 LWIP_UDP 为 1,则在编译的时候与 UDP 类型内存池就会被建立;定义 LWIP_TCP 为 1,则在编译的时候与 TCP 类型内存池就会被建立。另外,还有很多其他类型的内存池,如专门存放网络包数据信息的 PBUF_POOL、还有上面讲解动态内存堆分配策略时提到的 CUSTOM_POOLS 等等等等。某种类型的 POOL 其单个大小是固定的,而分配该类 POOL 的个数是可以用户配置的,用户应该根据协议栈实际使用状况进行配置。把协议栈中所有的 POOL 挨个放到一起,并把它们放在一片连续的内存区域,这呈现给用户的就是一个大的缓冲池。所以,所谓的缓冲池的内部组织应该是这样的:开始处放了 A 类型的 POOL 池 a 个,紧接着放上 B 类型的 POOL 池 b 个,再接着放上 C 类型的POOL 池 c 个….直至最后 N 类型的 POOL 池 n 个。这一点很像 UC/OSII 中进程控制块和事件控制块,先开辟一堆各种类型的放那,你要用直接来取就是了。注意,这里的分配必须是以单个缓冲池为基本单位的,在这样的情况下,可能导致内存浪费的情况。这是很明显的啊,不解释。

#具体设计
下面我到具体情景来分析

##蓝牙协议栈分层
低功耗蓝牙协议栈由许多层组成,如下图所示:

蓝牙协议栈框架

具体每一层是干嘛的,大家通过百度、谷歌都可以了解。

##内存分配
       现在需要当协议栈工作的时候,需要进行内存分配,也就是需要为各种连接参数和服务等做一个内存管理,例如HCI连接参数,l2cap服务,l2cap信道等。

##进入正题
废话讲了这么多现在才进入正题:

内存创建函数

内存的分配是由一个一个链表的节点串起来的
定义的数据结构如下:

typedef struct node {
	struct node * next;
} node_t;

然后是内存创建函数:memory_pool_create

void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size){
    node_t *free_blocks = (node_t*) pool;
    char   *mem_ptr = (char *) storage;
    int i;
    
    //为所有blocks创建单链表
    free_blocks->next = NULL;
    for (i = 0 ; i < count ; i++){
        memory_pool_free(pool, mem_ptr);
        mem_ptr += block_size;
    }
}

其中pool是一个指向内存池最后一个节点的指针,storage是结构体数组的首地址,count是结构数组的成员数,block_size是结构体分配的内存大小(按照字节计数)。当调用memory_pool_create()时,过程如下图所示:
这里写图片描述

在内存创建函数中有一个memory_pool_free()函数,这个函数的作用是将结构体数组的首地址mem_ptr(=storage)链接到内存池中。具体函数如下:

void memory_pool_free(memory_pool_t *pool, void * block){
	node_t *free_blocks = (node_t*)pool;
	node_t *node = (node_t*)block;

	// 如果节点已经在链表中就退出
	node_t * it;
	for (it = free_blocks->next; it; it = it->next){
		if (it == node) {
			return;
		}
	}

	// 将结构体数组的首地址加入到链表中,如果结构体数组元素有很多个,就一个一个的加进去
	node->next = free_blocks->next;
	free_blocks->next = node;
}

###获取内存函数
那当需要内存的时候怎去获取呢,这就需要一个内存获取函数,如下:

void * memory_pool_get(memory_pool_t *pool){
	node_t *free_blocks = (node_t*)pool;

	if (!free_blocks->next) return NULL;

	// 移除头节点
	node_t *node = free_blocks->next;
	free_blocks->next = node->next;

	//返回头结点
	return (void*)node;
}

下面就到真正使用的时候了,如果有需要可以设个断点,然后自己看看内存池是怎么生长和取出来使用的
###具体例子

#include <stdio.h>
#include <stddef.h>

typedef void* memory_pool_t;

typedef struct node {
	struct node * next;
} node_t;

void memory_pool_free(memory_pool_t *pool, void * block){
	node_t *free_blocks = (node_t*)pool;
	node_t *node = (node_t*)block;

	//如果节点已经在链表中就退出
	node_t * it;
	for (it = free_blocks->next; it; it = it->next){
		if (it == node) {
			return;
		}
	}

	// 将结构体数组的首地址加入到链表中,如果结构体数组元素有很多个,就一个一个的加进去
	node->next = free_blocks->next;
	free_blocks->next = node;
}

void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size){
	node_t *free_blocks = (node_t*)pool;
	char   *mem_ptr = (char *)storage;
	int i;

	// 为所有blocks创建单链表
	free_blocks->next = NULL;
	for (i = 0; i < count; i++){
		memory_pool_free(pool, mem_ptr);
		mem_ptr += block_size;
	}
}

void * memory_pool_get(memory_pool_t *pool){
	node_t *free_blocks = (node_t*)pool;

	if (!free_blocks->next) return NULL;

	// 移除头节点
	node_t *node = free_blocks->next;
	free_blocks->next = node->next;

	return (void*)node;
}




typedef struct 
{
	int a;
	char b;
	char c;
}hci_connection_t;
 
static hci_connection_t hci_connection_storage[2];
static memory_pool_t hci_connection_pool;

typedef struct
{
	int a;
	char b;
	int  c;
}l2cap_connection_t;

static l2cap_connection_t l2cap_connection_storage[3];
static memory_pool_t l2cap_connection_pool;

hci_connection_t * btstack_memory_hci_connection_get(void){
	return (hci_connection_t *)memory_pool_get(&hci_connection_pool);
}
void btstack_memory_hci_connection_free(hci_connection_t *hci_connection){
	memory_pool_free(&hci_connection_pool, hci_connection);
}

l2cap_connection_t * btstack_memory_l2cap_connection_get(void){
	return (l2cap_connection_t *)memory_pool_get(&l2cap_connection_pool);
}
void btstack_memory_l2cap_connection_free(hci_connection_t *l2cap_connection){
	memory_pool_free(&l2cap_connection_pool,l2cap_connection);
}

int main()
{
	memory_pool_create(&hci_connection_pool, hci_connection_storage, 2, sizeof(hci_connection_t));
	memory_pool_create(&l2cap_connection_pool, l2cap_connection_storage, 3, sizeof(l2cap_connection_t));

	hci_connection_t *con = btstack_memory_hci_connection_get();
	l2cap_connection_t *lcon = btstack_memory_l2cap_connection_get();

	return 0;
}

这时候,内存池是这样分布的:
这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值