前提介绍
静态内存池的具体作用以及为什么要使用静态内存池就不做介绍,不清楚的博友可以网上找找介绍或者AI简单了解一下,这里的实现为固定大小的静态内存池,仅为我自己的一个想法,如果有更好的方法与实现逻辑可在评论区指正。
整体思路流程
假设有100个字节的大小,我需要按照10个字节为一块,一共10组。每次我做申请的时候我就只能获取其中一组,使用结束后释放又将其返回到之前容器里面。
这里的容器我打算使用链表的数据结构将其管理,10组数据我的链表结点就是10个(除开头结点),而链表本身也需要空间来存储,这里就需要使用malloc申请了吗?当然不是,以下就是我这个设计的妙点。
我这里的内存池空间是一个全局变量数组,它的生命周期是整个程序。当空闲块没有使用时,这块空间可以用来当作链表的结点,如下图:
这里的一个块空间大小是上面描述的10个字节,而每个结点则就只是一个指针,如果指针按照4字节大小的话,那么实际占用的空间就只是前4个字节,就必须保证每个块的大小至少为4个字节才能正常构建链表。
那么正常构建完成链表后就会是如下所示:
这样就把一个连续的全局变量数组构建成了一个链表。
此时,如果需要申请一块内存,就会使用头减的方式把head的下一个结点首地址分配出去,既分配结点1出去,然后把head的下一个结点指向结点2。
释放的方法和申请类似,使用头插的方式把释放的地址重新放入结点中。
示例流程:
部分代码实现
这里我实现了四个对应的API函数:
mem_pool_init:负责对这块全局变量内存进行链表化以及相关特征值的计算,并返回
相关handle等。
mem_pool_status_t mem_pool_init(stc_mem_pool_handle_t *handle, void *mem_pool, uint16_t mem_pool_size, uint16_t node_lenth) { if(handle == NULL || mem_pool == NULL || mem_pool_size == 0 || node_lenth == 0 || mem_pool_size%node_lenth != 0) { mem_pool_log("mem_pool_init error, handle is null or mem_pool is null or mem_pool_size is 0 or node_lenth is 0 or mem_pool_size mod node_lenth!= 0\n"); return MEM_POOL_PRARMETER_ERROR; } if(handle->is_init) { mem_pool_log("mem_pool_init error, handle is init\n"); return MEM_POOL_IS_INIT; } handle->list_head.next_node = NULL; handle->list_lenth = node_lenth; handle->crr_remaining_lenth = node_lenth; handle->mem_pool_size = mem_pool_size; handle->node_size = mem_pool_size/node_lenth; handle->is_init = 1; handle->mem_start_addr = (void *)mem_pool; //mem_pool_log("mem_pool start addr:%x, mem_pool_size:%d, node_lenth:%d, node_size:%d\n", handle->mem_start_addr, handle->mem_pool_size, handle->list_lenth, handle->node_size); uint16_t node_size = mem_pool_size/node_lenth; uint8_t *start_addr = (uint8_t *)mem_pool; mem_node_t *crr_node = &handle->list_head; for(int i = 0; i < handle->list_lenth; i++) { crr_node->next_node = (mem_node_t *)(start_addr + i * node_size); crr_node->next_node->next_node = NULL; crr_node = crr_node->next_node; } #if USE_RTOS_SYS mem_pool_mutex_init(handle->mutex); #endif return MEM_POOL_SUCCESS; }mem_pool_deinit:销毁内存池对handle进行一些清空等。
mem_pool_status_t mem_pool_deinit(stc_mem_pool_handle_t *handle) { // 如果使用了rtos,可以使用一个临界区来保护内存池的初始化和释放 if(handle == NULL || handle->is_init == 0) { mem_pool_log("mem_pool_deinit error, handle is null or handle is not init\n"); return MEM_POOL_PRARMETER_ERROR; } handle->list_head.next_node = NULL; handle->crr_remaining_lenth = 0; handle->is_init = 0; handle->mem_pool_size = 0; handle->node_size = 0; handle->list_lenth = 0; handle->mem_start_addr = NULL; #if USE_RTOS_SYS mem_pool_mutex_deinit(handle->mutex); #endif return MEM_POOL_SUCCESS; }mem_pool_alloc:通过传入handle申请内存空间。
void *mem_pool_alloc(stc_mem_pool_handle_t *handle) { if(handle == NULL || handle->is_init == 0) { mem_pool_log("mem_pool_alloc error, handle is null or handle is not init\n"); return NULL; } #if USE_RTOS_SYS MEM_POOL_MUTEX_LOCK(handle->mutex); #endif void *ret = NULL; mem_node_t *crr_node = handle->list_head.next_node; if(crr_node == NULL) { mem_pool_log("mem_pool_alloc error, crr_node is null\n"); return NULL; } ret = (void *)crr_node; handle->list_head.next_node = crr_node->next_node; handle->crr_remaining_lenth--; #if USE_RTOS_SYS MEM_POOL_MUTEX_UNLOCK(handle->mutex); #endif return ret; }mem_pool_free:通过传入handle与释放地址做相关释放。
mem_pool_status_t mem_pool_free(stc_mem_pool_handle_t *handle, void *mem) { if(handle == NULL || mem == NULL) { mem_pool_log("mem_pool_free error, handle is null or handle is not init or mem is null\n"); return MEM_POOL_PRARMETER_ERROR; } if(handle->is_init == 0) { mem_pool_log("mem_pool_free error, handle is not init\n"); return MEM_POOL_IS_NOT_INIT; } uintptr_t mem_addr = (uintptr_t)mem; uintptr_t pool_start = (uintptr_t)(handle->mem_start_addr); uintptr_t pool_end = pool_start + handle->mem_pool_size; //if(mem < handle->mem_start_addr || mem >= (handle->mem_start_addr + handle->mem_pool_size)) { if(mem_addr < pool_start || mem_addr >= pool_end) { mem_pool_log("mem_pool_free error, mem is out of mem_pool\n"); return MEM_POOL_PRARMETER_ERROR; } //if((mem - handle->mem_start_addr) % handle->node_size != 0) { if((mem_addr - pool_start) % handle->node_size != 0) { mem_pool_log("mem_pool_free error, this not node addr\n"); return MEM_POOL_PRARMETER_ERROR; } #if USE_RTOS_SYS MEM_POOL_MUTEX_LOCK(handle->mutex); #endif memset(mem, 0, handle->node_size); mem_node_t *free_node = (mem_node_t *)mem; free_node->next_node = handle->list_head.next_node; handle->list_head.next_node = free_node; handle->crr_remaining_lenth++; #if USE_RTOS_SYS MEM_POOL_MUTEX_UNLOCK(handle->mutex); #endif return MEM_POOL_SUCCESS; }示例应用
这里我写了一个简单的测试用例,使用260个字节全局变量,分成10个块,每个块空间占用26给字节。
#define TEST_MEM_POOL_NODE_LENTH (10) static uint8_t test_data[260]; static stc_mem_pool_handle_t test_handle; typedef struct { char name[10]; char number[11]; char sex[2]; char age[3]; long long c; } test_t; typedef struct { test_t va; int a; } cs; int main(void) { printf("%d,%d\n", sizeof(cs), sizeof(test_t)); mem_pool_init(&test_handle, test_data, sizeof(test_data), TEST_MEM_POOL_NODE_LENTH); test_node_info(&test_handle); test_t *xiao_m = (test_t *)mem_pool_alloc(&test_handle); if(xiao_m == NULL) { printf("mem_pool_alloc error %p\n", xiao_m); } test_t *xiao_h = (test_t *)mem_pool_alloc(&test_handle); if(xiao_h == NULL) { printf("mem_pool_alloc error %p\n", xiao_h); } test_t *xiao_l = (test_t *)mem_pool_alloc(&test_handle); if(xiao_l == NULL) { printf("mem_pool_alloc error %p\n", xiao_l); } printf("xiao_m: %p\n", xiao_m); printf("xiao_h: %p\n", xiao_h); printf("xiao_l: %p\n", xiao_l); memcpy(xiao_m->name, "xiao_m", sizeof("xiao_m")); memcpy(xiao_h->name, "xiao_h", sizeof("xiao_h")); memcpy(xiao_l->name, "xiao_l", sizeof("xiao_l")); memcpy(xiao_m->number, "10086", sizeof("10086")); memcpy(xiao_h->number, "1008611", sizeof("1008611")); memcpy(xiao_l->number, "10010", sizeof("10010")); memcpy(xiao_m->age, "20", sizeof("20")); memcpy(xiao_h->age, "19", sizeof("19")); memcpy(xiao_l->age, "18", sizeof("18")); memcpy(xiao_m->sex, "n", sizeof("n")); memcpy(xiao_h->sex, "n", sizeof("n")); memcpy(xiao_l->sex, "l", sizeof("l")); printf("xiao_m->name: %s\n", xiao_m->name); printf("xiao_h->name: %s\n", xiao_h->name); printf("xiao_l->name: %s\n", xiao_l->name); printf("xiao_m->number: %s\n", xiao_m->number); printf("xiao_h->number: %s\n", xiao_h->number); printf("xiao_l->number: %s\n", xiao_l->number); printf("xiao_m->sex: %s\n", xiao_m->sex); printf("xiao_h->sex: %s\n", xiao_h->sex); printf("xiao_l->sex: %s\n", xiao_l->sex); printf("xiao_m->age: %s\n", xiao_m->age); printf("xiao_h->age: %s\n", xiao_h->age); printf("xiao_l->age: %s\n", xiao_l->age); test_node_info(&test_handle); if(mem_pool_free(&test_handle, xiao_m) != MEM_POOL_SUCCESS) { printf("mem_pool_free error %p\n", xiao_m); } if(mem_pool_free(&test_handle, xiao_h) != MEM_POOL_SUCCESS) { printf("mem_pool_free error %p\n", xiao_h); } if(mem_pool_free(&test_handle, xiao_l) != MEM_POOL_SUCCESS) { printf("mem_pool_free error %p\n", xiao_l); } test_node_info(&test_handle); return 0; } void test_node_info(stc_mem_pool_handle_t *handle) { printf("\r\n"); mem_node_t *crr_node = handle->list_head.next_node; for(int i = 0; i < handle->crr_remaining_lenth; i++) { if(crr_node == NULL) { //printf("crr_node is null\n"); break; } printf("crr_addr:%p\n", crr_node); crr_node = crr_node->next_node; } }后面还有释放后的链表打印,图太长了就不展示了。
最后,我使用我的这套静态内存池的方案自己写了一个AT指令驱动库,使用ESP8266 WIFI 模块做的例子。使用收发循环队列+静态内存池+UART DMA接收的方案做的,支持URC专用回调与注册,已经跑通了onenet的mqtt方案以及http ota升级,证明这套静态内存池方案可用。上面说的后续我都会以博客的方式分享出来。